midict.erl 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. %% Author: Sergei Polkovnikov <serge.polkovnikov@gmail.com>
  2. %% Created: Jan 8, 2010
  3. %% Description: Multi indices dictionary
  4. -module(midict).
  5. -author("Sergei Polkovnikov <serge.polkovnikov@gmail.com>").
  6. %%
  7. %% Include files
  8. %%
  9. -include_lib("eunit/include/eunit.hrl").
  10. %%
  11. %% Exported Functions
  12. %%
  13. -export([
  14. new/0,
  15. store/4,
  16. find/2,
  17. fetch/2,
  18. geti/3,
  19. erase/2,
  20. size/1,
  21. to_list/1,
  22. from_list/1,
  23. fetch_keys/1,
  24. all_values/1
  25. ]).
  26. -record(midict,
  27. {
  28. keyd,
  29. indds
  30. }).
  31. -type midict() :: #midict{}.
  32. %%
  33. %% API Functions
  34. %%
  35. -spec new() -> midict().
  36. %% @spec new() -> midict()
  37. %% @doc Creates empty midict.
  38. %% @end
  39. new() ->
  40. #midict{
  41. keyd = dict:new(),
  42. indds = dict:new()
  43. }.
  44. -spec store(term(), term(), list(), midict()) -> midict().
  45. %% @spec store(Key, Value, Indices, MIDict) -> MIDict2
  46. %% @doc Puts the entity to the midict.
  47. %% Types:
  48. %% Key = Value = term()
  49. %% Indices = [{Index, IndValue}]
  50. %% Index = IndValue = term()
  51. %% MIDict = MIDict2 = midict()
  52. %% @end
  53. store(Key, Value, Indices, #midict{keyd = KeyD, indds = IndD}) ->
  54. Obj = {Key, Value, Indices},
  55. IndD1 = case dict:find(Key, KeyD) of
  56. {ok, OldObj} ->
  57. del_indices(OldObj, IndD);
  58. error ->
  59. IndD
  60. end,
  61. NewKeyD = dict:store(Key, Obj, KeyD),
  62. NewIndD = add_indices(Obj, IndD1),
  63. #midict{keyd = NewKeyD, indds = NewIndD}.
  64. -spec find(term(), midict()) -> {ok, term()} | error.
  65. %% @spec find(Key, MIDict) -> {ok, Value} | error
  66. %% @doc Get an entity from the dict by the key.
  67. %% Types:
  68. %% Key = Value = term()
  69. %% MIDict = midict()
  70. %% @end
  71. find(Key, #midict{keyd = KeyD}) ->
  72. case dict:find(Key, KeyD) of
  73. {ok, {_, Value, _}} ->
  74. {ok, Value};
  75. error ->
  76. error
  77. end.
  78. -spec fetch(term(), midict()) -> term().
  79. %% @spec fetch(Key, MIDict) -> Value}
  80. %% @doc Fetch an entity from the dict by the key. An exception
  81. %% will arise if there is no entity with the key.
  82. %% Types:
  83. %% Key = Value = term()
  84. %% MIDict = midict()
  85. %% @end
  86. fetch(Key, #midict{keyd = KeyD}) ->
  87. {_, Value, _} = dict:fetch(Key, KeyD),
  88. Value.
  89. -spec geti(term(), term(), midict()) -> [term()].
  90. %% @spec geti(IndexVal, Index, MIDict) -> Values
  91. %% @doc Gets entities from the midict by the index.
  92. %% Types:
  93. %% IndexVal = Index = term()
  94. %% MIDict = midict()
  95. %% Values = [Value]
  96. %% Value = term()
  97. %% @end
  98. geti(IVal, Index, #midict{indds = IndD}) ->
  99. case dict:find({Index, IVal}, IndD) of
  100. {ok, Set} ->
  101. [Value || {_, Value, _} <- sets:to_list(Set)];
  102. error ->
  103. []
  104. end.
  105. -spec erase(term(), midict()) -> midict().
  106. %% @spec erase(Key, MIDict) -> MIDict2
  107. %% @doc Delete an entity from the midict by the key.
  108. %% Types:
  109. %% Key = term()
  110. %% MIDict = MIDict2 = midict()
  111. %% @end
  112. erase(Key, #midict{keyd = KeyD, indds = IndD}) ->
  113. NewIndD = case dict:find(Key, KeyD) of
  114. {ok, Obj} ->
  115. del_indices(Obj, IndD);
  116. error ->
  117. IndD
  118. end,
  119. NewKeyD = dict:erase(Key, KeyD),
  120. #midict{keyd = NewKeyD, indds = NewIndD}.
  121. -spec size(midict()) -> non_neg_integer().
  122. %% @spec size(midict()) -> integer()
  123. %% @doc Returns number of entities in the dict.
  124. %% @end
  125. size(#midict{keyd = KeyD}) ->
  126. dict:size(KeyD).
  127. -spec to_list(midict()) -> list({term(), term(), list()}).
  128. %% @spec to_list(midict()) -> List
  129. %% @doc Convert the midict to a list.
  130. %% Types:
  131. %% List = {Key, Value, Indices}
  132. %% Key = Value = term()
  133. %% Indices = [{Index, IndValue}]
  134. %% Index = IndValue = term()
  135. %% @end
  136. to_list(#midict{keyd = KeyD}) ->
  137. [Obj || {_, Obj} <- dict:to_list(KeyD)].
  138. -spec from_list(list({term(), term(), list()})) -> midict().
  139. %% @spec from_list(List) -> midict()
  140. %% @doc Convert the list to a midict.
  141. %% Types:
  142. %% List = {Key, Value, Indices}
  143. %% Key = Value = term()
  144. %% Indices = [{Index, IndValue}]
  145. %% Index = IndValue = term()
  146. %% @end
  147. from_list([]) ->
  148. midict:new();
  149. from_list([{Key, Value, Indices} | Rest]) ->
  150. midict:store(Key, Value, Indices, from_list(Rest)).
  151. -spec fetch_keys(midict()) -> list(term()).
  152. %% @spec fetch_keys(midict()) -> Keys
  153. %% @doc Fetches all keys of the midict.
  154. %% Types:
  155. %% Keys = [Key]
  156. %% Key = term()
  157. %% @end
  158. fetch_keys(#midict{keyd = KeyD}) ->
  159. dict:fetch_keys(KeyD).
  160. -spec all_values(midict()) -> list(term()).
  161. %% @spec all_values(midict()) -> Values
  162. %% @doc Fetches all enities values of the midict.
  163. %% Types:
  164. %% Values = [Value]
  165. %% Value = term()
  166. %% @end
  167. all_values(#midict{keyd = KeyD}) ->
  168. [Val || {_, {_, Val, _}} <- dict:to_list(KeyD)].
  169. %%
  170. %% Local Functions
  171. %%
  172. add_indices({_, _, Indices} = Obj, IndD) ->
  173. add_indices(Obj, IndD, Indices).
  174. add_indices(_Obj, IndD, []) ->
  175. IndD;
  176. add_indices(Obj, IndD, [I | Indices]) when tuple_size(I) == 2->
  177. CurSet = get_set(I, IndD),
  178. NewSet = sets:add_element(Obj, CurSet),
  179. NewIndD = put_set(NewSet, I, IndD),
  180. add_indices(Obj, NewIndD, Indices).
  181. del_indices({_, _, Indices} = Obj, IndD) ->
  182. del_indices(Obj, IndD, Indices).
  183. del_indices(_Obj, IndD, []) ->
  184. IndD;
  185. del_indices(Obj, IndD, [I | Indices]) ->
  186. CurSet = get_set(I, IndD),
  187. NewSet = sets:del_element(Obj, CurSet),
  188. NewIndD = put_set(NewSet, I, IndD),
  189. del_indices(Obj, NewIndD, Indices).
  190. get_set(Index, IndDs) ->
  191. case dict:find(Index, IndDs) of
  192. {ok, Set} -> Set;
  193. error -> sets:new()
  194. end.
  195. put_set(Set, Index, IndDs) ->
  196. case sets:size(Set) of
  197. 0 -> dict:erase(Index, IndDs);
  198. _ -> dict:store(Index, Set, IndDs)
  199. end.
  200. %%
  201. test_test_() ->
  202. [
  203. {"new",
  204. [
  205. ?_assert(begin midict:new(), true end)
  206. ]},
  207. {"store",
  208. [
  209. ?_assert(begin midict:store("xxx", value, [{ccc, 5}, {ttt,fff}], x()), true end),
  210. ?_assert(begin midict:store("xxx", value, [{ccc, 5}, {ttt,fff}], x2()), true end)
  211. ]},
  212. {"find",
  213. [
  214. ?_assertEqual({ok, {record1}}, midict:find("Key1", x5())),
  215. ?_assertEqual({ok, record3} , midict:find(<<"Key3">>, x5())),
  216. ?_assertEqual({ok, {record2, hello}} , midict:find("Key2", x5())),
  217. ?_assertEqual(error , midict:find(<<"UnknownKey">>, x5()))
  218. ]},
  219. {"geti",
  220. [
  221. ?_assertEqual(lists:sort([{record1}, {record2, hello}]), lists:sort(midict:geti(5, table, x5()))),
  222. ?_assertEqual(lists:sort([record3, "record4"]), lists:sort(midict:geti({3,1}, table_seat, x5()))),
  223. ?_assertEqual([{record2, hello}], midict:geti({5, 3}, table_seat, x5())),
  224. ?_assertEqual(["record4"], midict:geti(true, haha, x5())),
  225. ?_assertEqual([], midict:geti(4, table_seat, x5())),
  226. ?_assertEqual([], midict:geti(100500, xxx, x5()))
  227. ]},
  228. {"erase",
  229. [
  230. ?_assertEqual(error, midict:find("Key2", midict:erase("Key2", x5())))
  231. ]},
  232. {"size",
  233. [
  234. ?_assertEqual(0, midict:size(x())),
  235. ?_assertEqual(4, midict:size(x5())),
  236. ?_assertEqual(3, midict:size(x6()))
  237. ]},
  238. {"fetch_keys",
  239. [
  240. ?_assertEqual(lists:sort(["Key1", "Key2", <<"Key3">>, key4]), lists:sort(midict:fetch_keys(x5())))
  241. ]}
  242. ].
  243. x() -> midict:new().
  244. x2() -> midict:store("Key1", {record1}, [{table, 5}, {table_seat, {5, 2}}], x()).
  245. x3() -> midict:store("Key2", {record2, hello}, [{table, 5}, {table_seat, {5, 3}}], x2()).
  246. x4() -> midict:store(<<"Key3">>, record3, [{table, 3}, {table_seat, {3, 1}}], x3()).
  247. x5() -> midict:store(key4, "record4", [{table, 2}, {table_seat, {3, 1}}, {haha, true}], x4()).
  248. x6() -> midict:erase("Key2", x5()).