immutable-ruby 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. checksums.yaml +7 -0
  2. data/lib/immutable.rb +9 -0
  3. data/lib/immutable/core_ext.rb +2 -0
  4. data/lib/immutable/core_ext/enumerable.rb +11 -0
  5. data/lib/immutable/core_ext/io.rb +21 -0
  6. data/lib/immutable/deque.rb +254 -0
  7. data/lib/immutable/enumerable.rb +152 -0
  8. data/lib/immutable/hash.rb +841 -0
  9. data/lib/immutable/list.rb +1595 -0
  10. data/lib/immutable/nested.rb +75 -0
  11. data/lib/immutable/set.rb +583 -0
  12. data/lib/immutable/sorted_set.rb +1464 -0
  13. data/lib/immutable/trie.rb +338 -0
  14. data/lib/immutable/undefined.rb +5 -0
  15. data/lib/immutable/vector.rb +1539 -0
  16. data/lib/immutable/version.rb +5 -0
  17. data/spec/fixtures/io_spec.txt +3 -0
  18. data/spec/lib/immutable/core_ext/array_spec.rb +13 -0
  19. data/spec/lib/immutable/core_ext/enumerable_spec.rb +29 -0
  20. data/spec/lib/immutable/core_ext/io_spec.rb +28 -0
  21. data/spec/lib/immutable/deque/clear_spec.rb +33 -0
  22. data/spec/lib/immutable/deque/construction_spec.rb +29 -0
  23. data/spec/lib/immutable/deque/copying_spec.rb +19 -0
  24. data/spec/lib/immutable/deque/dequeue_spec.rb +34 -0
  25. data/spec/lib/immutable/deque/empty_spec.rb +39 -0
  26. data/spec/lib/immutable/deque/enqueue_spec.rb +27 -0
  27. data/spec/lib/immutable/deque/first_spec.rb +17 -0
  28. data/spec/lib/immutable/deque/inspect_spec.rb +23 -0
  29. data/spec/lib/immutable/deque/last_spec.rb +17 -0
  30. data/spec/lib/immutable/deque/marshal_spec.rb +33 -0
  31. data/spec/lib/immutable/deque/new_spec.rb +43 -0
  32. data/spec/lib/immutable/deque/pop_spec.rb +36 -0
  33. data/spec/lib/immutable/deque/pretty_print_spec.rb +23 -0
  34. data/spec/lib/immutable/deque/push_spec.rb +36 -0
  35. data/spec/lib/immutable/deque/random_modification_spec.rb +33 -0
  36. data/spec/lib/immutable/deque/shift_spec.rb +29 -0
  37. data/spec/lib/immutable/deque/size_spec.rb +19 -0
  38. data/spec/lib/immutable/deque/to_a_spec.rb +26 -0
  39. data/spec/lib/immutable/deque/to_ary_spec.rb +35 -0
  40. data/spec/lib/immutable/deque/to_list_spec.rb +24 -0
  41. data/spec/lib/immutable/deque/unshift_spec.rb +30 -0
  42. data/spec/lib/immutable/hash/all_spec.rb +53 -0
  43. data/spec/lib/immutable/hash/any_spec.rb +53 -0
  44. data/spec/lib/immutable/hash/assoc_spec.rb +51 -0
  45. data/spec/lib/immutable/hash/clear_spec.rb +42 -0
  46. data/spec/lib/immutable/hash/construction_spec.rb +38 -0
  47. data/spec/lib/immutable/hash/copying_spec.rb +13 -0
  48. data/spec/lib/immutable/hash/default_proc_spec.rb +72 -0
  49. data/spec/lib/immutable/hash/delete_spec.rb +39 -0
  50. data/spec/lib/immutable/hash/each_spec.rb +77 -0
  51. data/spec/lib/immutable/hash/each_with_index_spec.rb +29 -0
  52. data/spec/lib/immutable/hash/empty_spec.rb +43 -0
  53. data/spec/lib/immutable/hash/eql_spec.rb +69 -0
  54. data/spec/lib/immutable/hash/except_spec.rb +42 -0
  55. data/spec/lib/immutable/hash/fetch_spec.rb +57 -0
  56. data/spec/lib/immutable/hash/find_spec.rb +43 -0
  57. data/spec/lib/immutable/hash/flat_map_spec.rb +35 -0
  58. data/spec/lib/immutable/hash/flatten_spec.rb +98 -0
  59. data/spec/lib/immutable/hash/get_spec.rb +79 -0
  60. data/spec/lib/immutable/hash/has_key_spec.rb +31 -0
  61. data/spec/lib/immutable/hash/has_value_spec.rb +27 -0
  62. data/spec/lib/immutable/hash/hash_spec.rb +29 -0
  63. data/spec/lib/immutable/hash/inspect_spec.rb +30 -0
  64. data/spec/lib/immutable/hash/invert_spec.rb +30 -0
  65. data/spec/lib/immutable/hash/key_spec.rb +27 -0
  66. data/spec/lib/immutable/hash/keys_spec.rb +15 -0
  67. data/spec/lib/immutable/hash/map_spec.rb +45 -0
  68. data/spec/lib/immutable/hash/marshal_spec.rb +28 -0
  69. data/spec/lib/immutable/hash/merge_spec.rb +82 -0
  70. data/spec/lib/immutable/hash/min_max_spec.rb +45 -0
  71. data/spec/lib/immutable/hash/new_spec.rb +70 -0
  72. data/spec/lib/immutable/hash/none_spec.rb +48 -0
  73. data/spec/lib/immutable/hash/partition_spec.rb +35 -0
  74. data/spec/lib/immutable/hash/pretty_print_spec.rb +34 -0
  75. data/spec/lib/immutable/hash/put_spec.rb +102 -0
  76. data/spec/lib/immutable/hash/reduce_spec.rb +35 -0
  77. data/spec/lib/immutable/hash/reject_spec.rb +61 -0
  78. data/spec/lib/immutable/hash/reverse_each_spec.rb +27 -0
  79. data/spec/lib/immutable/hash/sample_spec.rb +13 -0
  80. data/spec/lib/immutable/hash/select_spec.rb +57 -0
  81. data/spec/lib/immutable/hash/size_spec.rb +51 -0
  82. data/spec/lib/immutable/hash/slice_spec.rb +44 -0
  83. data/spec/lib/immutable/hash/sort_spec.rb +26 -0
  84. data/spec/lib/immutable/hash/store_spec.rb +75 -0
  85. data/spec/lib/immutable/hash/take_spec.rb +35 -0
  86. data/spec/lib/immutable/hash/to_a_spec.rb +13 -0
  87. data/spec/lib/immutable/hash/to_hash_spec.rb +21 -0
  88. data/spec/lib/immutable/hash/update_in_spec.rb +79 -0
  89. data/spec/lib/immutable/hash/values_at_spec.rb +13 -0
  90. data/spec/lib/immutable/hash/values_spec.rb +23 -0
  91. data/spec/lib/immutable/list/add_spec.rb +25 -0
  92. data/spec/lib/immutable/list/all_spec.rb +57 -0
  93. data/spec/lib/immutable/list/any_spec.rb +49 -0
  94. data/spec/lib/immutable/list/append_spec.rb +38 -0
  95. data/spec/lib/immutable/list/at_spec.rb +29 -0
  96. data/spec/lib/immutable/list/break_spec.rb +69 -0
  97. data/spec/lib/immutable/list/cadr_spec.rb +38 -0
  98. data/spec/lib/immutable/list/chunk_spec.rb +28 -0
  99. data/spec/lib/immutable/list/clear_spec.rb +24 -0
  100. data/spec/lib/immutable/list/combination_spec.rb +33 -0
  101. data/spec/lib/immutable/list/compact_spec.rb +34 -0
  102. data/spec/lib/immutable/list/compare_spec.rb +30 -0
  103. data/spec/lib/immutable/list/cons_spec.rb +25 -0
  104. data/spec/lib/immutable/list/construction_spec.rb +110 -0
  105. data/spec/lib/immutable/list/copying_spec.rb +19 -0
  106. data/spec/lib/immutable/list/count_spec.rb +36 -0
  107. data/spec/lib/immutable/list/cycle_spec.rb +28 -0
  108. data/spec/lib/immutable/list/delete_at_spec.rb +18 -0
  109. data/spec/lib/immutable/list/delete_spec.rb +16 -0
  110. data/spec/lib/immutable/list/drop_spec.rb +30 -0
  111. data/spec/lib/immutable/list/drop_while_spec.rb +38 -0
  112. data/spec/lib/immutable/list/each_slice_spec.rb +51 -0
  113. data/spec/lib/immutable/list/each_spec.rb +40 -0
  114. data/spec/lib/immutable/list/each_with_index_spec.rb +28 -0
  115. data/spec/lib/immutable/list/empty_spec.rb +23 -0
  116. data/spec/lib/immutable/list/eql_spec.rb +61 -0
  117. data/spec/lib/immutable/list/fill_spec.rb +49 -0
  118. data/spec/lib/immutable/list/find_all_spec.rb +70 -0
  119. data/spec/lib/immutable/list/find_index_spec.rb +35 -0
  120. data/spec/lib/immutable/list/find_spec.rb +42 -0
  121. data/spec/lib/immutable/list/flat_map_spec.rb +51 -0
  122. data/spec/lib/immutable/list/flatten_spec.rb +30 -0
  123. data/spec/lib/immutable/list/grep_spec.rb +46 -0
  124. data/spec/lib/immutable/list/group_by_spec.rb +41 -0
  125. data/spec/lib/immutable/list/hash_spec.rb +21 -0
  126. data/spec/lib/immutable/list/head_spec.rb +19 -0
  127. data/spec/lib/immutable/list/include_spec.rb +35 -0
  128. data/spec/lib/immutable/list/index_spec.rb +33 -0
  129. data/spec/lib/immutable/list/indices_spec.rb +61 -0
  130. data/spec/lib/immutable/list/init_spec.rb +28 -0
  131. data/spec/lib/immutable/list/inits_spec.rb +28 -0
  132. data/spec/lib/immutable/list/insert_spec.rb +46 -0
  133. data/spec/lib/immutable/list/inspect_spec.rb +29 -0
  134. data/spec/lib/immutable/list/intersperse_spec.rb +28 -0
  135. data/spec/lib/immutable/list/join_spec.rb +63 -0
  136. data/spec/lib/immutable/list/last_spec.rb +23 -0
  137. data/spec/lib/immutable/list/ltlt_spec.rb +19 -0
  138. data/spec/lib/immutable/list/map_spec.rb +45 -0
  139. data/spec/lib/immutable/list/maximum_spec.rb +39 -0
  140. data/spec/lib/immutable/list/merge_by_spec.rb +51 -0
  141. data/spec/lib/immutable/list/merge_spec.rb +59 -0
  142. data/spec/lib/immutable/list/minimum_spec.rb +39 -0
  143. data/spec/lib/immutable/list/multithreading_spec.rb +47 -0
  144. data/spec/lib/immutable/list/none_spec.rb +47 -0
  145. data/spec/lib/immutable/list/one_spec.rb +49 -0
  146. data/spec/lib/immutable/list/partition_spec.rb +115 -0
  147. data/spec/lib/immutable/list/permutation_spec.rb +55 -0
  148. data/spec/lib/immutable/list/pop_spec.rb +25 -0
  149. data/spec/lib/immutable/list/product_spec.rb +23 -0
  150. data/spec/lib/immutable/list/reduce_spec.rb +53 -0
  151. data/spec/lib/immutable/list/reject_spec.rb +45 -0
  152. data/spec/lib/immutable/list/reverse_spec.rb +34 -0
  153. data/spec/lib/immutable/list/rotate_spec.rb +36 -0
  154. data/spec/lib/immutable/list/sample_spec.rb +13 -0
  155. data/spec/lib/immutable/list/select_spec.rb +70 -0
  156. data/spec/lib/immutable/list/size_spec.rb +25 -0
  157. data/spec/lib/immutable/list/slice_spec.rb +229 -0
  158. data/spec/lib/immutable/list/sorting_spec.rb +46 -0
  159. data/spec/lib/immutable/list/span_spec.rb +76 -0
  160. data/spec/lib/immutable/list/split_at_spec.rb +43 -0
  161. data/spec/lib/immutable/list/subsequences_spec.rb +23 -0
  162. data/spec/lib/immutable/list/sum_spec.rb +23 -0
  163. data/spec/lib/immutable/list/tail_spec.rb +30 -0
  164. data/spec/lib/immutable/list/tails_spec.rb +28 -0
  165. data/spec/lib/immutable/list/take_spec.rb +30 -0
  166. data/spec/lib/immutable/list/take_while_spec.rb +46 -0
  167. data/spec/lib/immutable/list/to_a_spec.rb +39 -0
  168. data/spec/lib/immutable/list/to_ary_spec.rb +41 -0
  169. data/spec/lib/immutable/list/to_list_spec.rb +19 -0
  170. data/spec/lib/immutable/list/to_set_spec.rb +17 -0
  171. data/spec/lib/immutable/list/transpose_spec.rb +19 -0
  172. data/spec/lib/immutable/list/union_spec.rb +31 -0
  173. data/spec/lib/immutable/list/uniq_spec.rb +35 -0
  174. data/spec/lib/immutable/list/zip_spec.rb +23 -0
  175. data/spec/lib/immutable/nested/construction_spec.rb +95 -0
  176. data/spec/lib/immutable/set/add_spec.rb +75 -0
  177. data/spec/lib/immutable/set/all_spec.rb +51 -0
  178. data/spec/lib/immutable/set/any_spec.rb +51 -0
  179. data/spec/lib/immutable/set/clear_spec.rb +33 -0
  180. data/spec/lib/immutable/set/compact_spec.rb +30 -0
  181. data/spec/lib/immutable/set/construction_spec.rb +18 -0
  182. data/spec/lib/immutable/set/copying_spec.rb +13 -0
  183. data/spec/lib/immutable/set/count_spec.rb +36 -0
  184. data/spec/lib/immutable/set/delete_spec.rb +71 -0
  185. data/spec/lib/immutable/set/difference_spec.rb +49 -0
  186. data/spec/lib/immutable/set/disjoint_spec.rb +25 -0
  187. data/spec/lib/immutable/set/each_spec.rb +45 -0
  188. data/spec/lib/immutable/set/empty_spec.rb +44 -0
  189. data/spec/lib/immutable/set/eqeq_spec.rb +103 -0
  190. data/spec/lib/immutable/set/eql_spec.rb +109 -0
  191. data/spec/lib/immutable/set/exclusion_spec.rb +47 -0
  192. data/spec/lib/immutable/set/find_spec.rb +35 -0
  193. data/spec/lib/immutable/set/first_spec.rb +28 -0
  194. data/spec/lib/immutable/set/flatten_spec.rb +46 -0
  195. data/spec/lib/immutable/set/grep_spec.rb +57 -0
  196. data/spec/lib/immutable/set/group_by_spec.rb +59 -0
  197. data/spec/lib/immutable/set/hash_spec.rb +22 -0
  198. data/spec/lib/immutable/set/include_spec.rb +60 -0
  199. data/spec/lib/immutable/set/inspect_spec.rb +47 -0
  200. data/spec/lib/immutable/set/intersect_spec.rb +25 -0
  201. data/spec/lib/immutable/set/intersection_spec.rb +52 -0
  202. data/spec/lib/immutable/set/join_spec.rb +64 -0
  203. data/spec/lib/immutable/set/map_spec.rb +59 -0
  204. data/spec/lib/immutable/set/marshal_spec.rb +28 -0
  205. data/spec/lib/immutable/set/maximum_spec.rb +36 -0
  206. data/spec/lib/immutable/set/minimum_spec.rb +36 -0
  207. data/spec/lib/immutable/set/new_spec.rb +53 -0
  208. data/spec/lib/immutable/set/none_spec.rb +47 -0
  209. data/spec/lib/immutable/set/one_spec.rb +47 -0
  210. data/spec/lib/immutable/set/partition_spec.rb +52 -0
  211. data/spec/lib/immutable/set/product_spec.rb +23 -0
  212. data/spec/lib/immutable/set/reduce_spec.rb +55 -0
  213. data/spec/lib/immutable/set/reject_spec.rb +50 -0
  214. data/spec/lib/immutable/set/reverse_each_spec.rb +38 -0
  215. data/spec/lib/immutable/set/sample_spec.rb +13 -0
  216. data/spec/lib/immutable/set/select_spec.rb +73 -0
  217. data/spec/lib/immutable/set/size_spec.rb +17 -0
  218. data/spec/lib/immutable/set/sorting_spec.rb +59 -0
  219. data/spec/lib/immutable/set/subset_spec.rb +51 -0
  220. data/spec/lib/immutable/set/sum_spec.rb +23 -0
  221. data/spec/lib/immutable/set/superset_spec.rb +51 -0
  222. data/spec/lib/immutable/set/to_a_spec.rb +30 -0
  223. data/spec/lib/immutable/set/to_list_spec.rb +35 -0
  224. data/spec/lib/immutable/set/to_set_spec.rb +19 -0
  225. data/spec/lib/immutable/set/union_spec.rb +63 -0
  226. data/spec/lib/immutable/sorted_set/above_spec.rb +51 -0
  227. data/spec/lib/immutable/sorted_set/add_spec.rb +62 -0
  228. data/spec/lib/immutable/sorted_set/at_spec.rb +24 -0
  229. data/spec/lib/immutable/sorted_set/below_spec.rb +51 -0
  230. data/spec/lib/immutable/sorted_set/between_spec.rb +51 -0
  231. data/spec/lib/immutable/sorted_set/clear_spec.rb +43 -0
  232. data/spec/lib/immutable/sorted_set/copying_spec.rb +20 -0
  233. data/spec/lib/immutable/sorted_set/delete_at_spec.rb +18 -0
  234. data/spec/lib/immutable/sorted_set/delete_spec.rb +89 -0
  235. data/spec/lib/immutable/sorted_set/difference_spec.rb +22 -0
  236. data/spec/lib/immutable/sorted_set/disjoint_spec.rb +25 -0
  237. data/spec/lib/immutable/sorted_set/drop_spec.rb +55 -0
  238. data/spec/lib/immutable/sorted_set/drop_while_spec.rb +34 -0
  239. data/spec/lib/immutable/sorted_set/each_spec.rb +28 -0
  240. data/spec/lib/immutable/sorted_set/empty_spec.rb +34 -0
  241. data/spec/lib/immutable/sorted_set/eql_spec.rb +120 -0
  242. data/spec/lib/immutable/sorted_set/exclusion_spec.rb +22 -0
  243. data/spec/lib/immutable/sorted_set/fetch_spec.rb +64 -0
  244. data/spec/lib/immutable/sorted_set/find_index_spec.rb +40 -0
  245. data/spec/lib/immutable/sorted_set/first_spec.rb +18 -0
  246. data/spec/lib/immutable/sorted_set/from_spec.rb +51 -0
  247. data/spec/lib/immutable/sorted_set/group_by_spec.rb +57 -0
  248. data/spec/lib/immutable/sorted_set/include_spec.rb +23 -0
  249. data/spec/lib/immutable/sorted_set/inspect_spec.rb +37 -0
  250. data/spec/lib/immutable/sorted_set/intersect_spec.rb +25 -0
  251. data/spec/lib/immutable/sorted_set/intersection_spec.rb +28 -0
  252. data/spec/lib/immutable/sorted_set/last_spec.rb +36 -0
  253. data/spec/lib/immutable/sorted_set/map_spec.rb +43 -0
  254. data/spec/lib/immutable/sorted_set/marshal_spec.rb +36 -0
  255. data/spec/lib/immutable/sorted_set/maximum_spec.rb +36 -0
  256. data/spec/lib/immutable/sorted_set/minimum_spec.rb +19 -0
  257. data/spec/lib/immutable/sorted_set/new_spec.rb +71 -0
  258. data/spec/lib/immutable/sorted_set/reverse_each_spec.rb +28 -0
  259. data/spec/lib/immutable/sorted_set/sample_spec.rb +13 -0
  260. data/spec/lib/immutable/sorted_set/select_spec.rb +61 -0
  261. data/spec/lib/immutable/sorted_set/size_spec.rb +17 -0
  262. data/spec/lib/immutable/sorted_set/slice_spec.rb +256 -0
  263. data/spec/lib/immutable/sorted_set/sorting_spec.rb +44 -0
  264. data/spec/lib/immutable/sorted_set/subset_spec.rb +47 -0
  265. data/spec/lib/immutable/sorted_set/superset_spec.rb +47 -0
  266. data/spec/lib/immutable/sorted_set/take_spec.rb +54 -0
  267. data/spec/lib/immutable/sorted_set/take_while_spec.rb +33 -0
  268. data/spec/lib/immutable/sorted_set/to_set_spec.rb +17 -0
  269. data/spec/lib/immutable/sorted_set/union_spec.rb +27 -0
  270. data/spec/lib/immutable/sorted_set/up_to_spec.rb +52 -0
  271. data/spec/lib/immutable/sorted_set/values_at_spec.rb +33 -0
  272. data/spec/lib/immutable/vector/add_spec.rb +67 -0
  273. data/spec/lib/immutable/vector/any_spec.rb +69 -0
  274. data/spec/lib/immutable/vector/assoc_spec.rb +45 -0
  275. data/spec/lib/immutable/vector/bsearch_spec.rb +65 -0
  276. data/spec/lib/immutable/vector/clear_spec.rb +33 -0
  277. data/spec/lib/immutable/vector/combination_spec.rb +81 -0
  278. data/spec/lib/immutable/vector/compact_spec.rb +29 -0
  279. data/spec/lib/immutable/vector/compare_spec.rb +31 -0
  280. data/spec/lib/immutable/vector/concat_spec.rb +34 -0
  281. data/spec/lib/immutable/vector/copying_spec.rb +20 -0
  282. data/spec/lib/immutable/vector/count_spec.rb +17 -0
  283. data/spec/lib/immutable/vector/delete_at_spec.rb +53 -0
  284. data/spec/lib/immutable/vector/delete_spec.rb +30 -0
  285. data/spec/lib/immutable/vector/drop_spec.rb +41 -0
  286. data/spec/lib/immutable/vector/drop_while_spec.rb +54 -0
  287. data/spec/lib/immutable/vector/each_index_spec.rb +40 -0
  288. data/spec/lib/immutable/vector/each_spec.rb +44 -0
  289. data/spec/lib/immutable/vector/each_with_index_spec.rb +39 -0
  290. data/spec/lib/immutable/vector/empty_spec.rb +41 -0
  291. data/spec/lib/immutable/vector/eql_spec.rb +76 -0
  292. data/spec/lib/immutable/vector/fetch_spec.rb +64 -0
  293. data/spec/lib/immutable/vector/fill_spec.rb +88 -0
  294. data/spec/lib/immutable/vector/first_spec.rb +18 -0
  295. data/spec/lib/immutable/vector/flat_map_spec.rb +50 -0
  296. data/spec/lib/immutable/vector/flatten_spec.rb +58 -0
  297. data/spec/lib/immutable/vector/get_spec.rb +74 -0
  298. data/spec/lib/immutable/vector/group_by_spec.rb +57 -0
  299. data/spec/lib/immutable/vector/include_spec.rb +30 -0
  300. data/spec/lib/immutable/vector/insert_spec.rb +68 -0
  301. data/spec/lib/immutable/vector/inspect_spec.rb +49 -0
  302. data/spec/lib/immutable/vector/join_spec.rb +58 -0
  303. data/spec/lib/immutable/vector/last_spec.rb +45 -0
  304. data/spec/lib/immutable/vector/length_spec.rb +45 -0
  305. data/spec/lib/immutable/vector/ltlt_spec.rb +65 -0
  306. data/spec/lib/immutable/vector/map_spec.rb +51 -0
  307. data/spec/lib/immutable/vector/marshal_spec.rb +31 -0
  308. data/spec/lib/immutable/vector/maximum_spec.rb +33 -0
  309. data/spec/lib/immutable/vector/minimum_spec.rb +33 -0
  310. data/spec/lib/immutable/vector/multiply_spec.rb +47 -0
  311. data/spec/lib/immutable/vector/new_spec.rb +50 -0
  312. data/spec/lib/immutable/vector/partition_spec.rb +52 -0
  313. data/spec/lib/immutable/vector/permutation_spec.rb +91 -0
  314. data/spec/lib/immutable/vector/pop_spec.rb +26 -0
  315. data/spec/lib/immutable/vector/product_spec.rb +70 -0
  316. data/spec/lib/immutable/vector/reduce_spec.rb +55 -0
  317. data/spec/lib/immutable/vector/reject_spec.rb +43 -0
  318. data/spec/lib/immutable/vector/repeated_combination_spec.rb +77 -0
  319. data/spec/lib/immutable/vector/repeated_permutation_spec.rb +93 -0
  320. data/spec/lib/immutable/vector/reverse_each_spec.rb +31 -0
  321. data/spec/lib/immutable/vector/reverse_spec.rb +21 -0
  322. data/spec/lib/immutable/vector/rindex_spec.rb +36 -0
  323. data/spec/lib/immutable/vector/rotate_spec.rb +73 -0
  324. data/spec/lib/immutable/vector/sample_spec.rb +13 -0
  325. data/spec/lib/immutable/vector/select_spec.rb +63 -0
  326. data/spec/lib/immutable/vector/set_spec.rb +174 -0
  327. data/spec/lib/immutable/vector/shift_spec.rb +27 -0
  328. data/spec/lib/immutable/vector/shuffle_spec.rb +43 -0
  329. data/spec/lib/immutable/vector/slice_spec.rb +240 -0
  330. data/spec/lib/immutable/vector/sorting_spec.rb +56 -0
  331. data/spec/lib/immutable/vector/sum_spec.rb +17 -0
  332. data/spec/lib/immutable/vector/take_spec.rb +42 -0
  333. data/spec/lib/immutable/vector/take_while_spec.rb +34 -0
  334. data/spec/lib/immutable/vector/to_a_spec.rb +41 -0
  335. data/spec/lib/immutable/vector/to_ary_spec.rb +34 -0
  336. data/spec/lib/immutable/vector/to_list_spec.rb +30 -0
  337. data/spec/lib/immutable/vector/to_set_spec.rb +21 -0
  338. data/spec/lib/immutable/vector/transpose_spec.rb +48 -0
  339. data/spec/lib/immutable/vector/uniq_spec.rb +76 -0
  340. data/spec/lib/immutable/vector/unshift_spec.rb +28 -0
  341. data/spec/lib/immutable/vector/update_in_spec.rb +82 -0
  342. data/spec/lib/immutable/vector/values_at_spec.rb +33 -0
  343. data/spec/lib/immutable/vector/zip_spec.rb +57 -0
  344. data/spec/lib/load_spec.rb +42 -0
  345. data/spec/spec_helper.rb +92 -0
  346. metadata +830 -0
@@ -0,0 +1,841 @@
1
+ require "immutable/undefined"
2
+ require "immutable/enumerable"
3
+ require "immutable/trie"
4
+ require "immutable/set"
5
+ require "immutable/vector"
6
+
7
+ module Immutable
8
+
9
+ # An `Immutable::Hash` maps a set of unique keys to corresponding values, much
10
+ # like a dictionary maps from words to definitions. Given a key, it can store
11
+ # and retrieve an associated value in constant time. If an existing key is
12
+ # stored again, the new value will replace the old. It behaves much like
13
+ # Ruby's built-in Hash, which we will call RubyHash for clarity. Like
14
+ # RubyHash, two keys that are `#eql?` to each other and have the same
15
+ # `#hash` are considered identical in an `Immutable::Hash`.
16
+ #
17
+ # An `Immutable::Hash` can be created in a couple of ways:
18
+ #
19
+ # Immutable::Hash.new(font_size: 10, font_family: 'Arial')
20
+ # Immutable::Hash[first_name: 'John', last_name: 'Smith']
21
+ #
22
+ # Any `Enumerable` object which yields two-element `[key, value]` arrays
23
+ # can be used to initialize an `Immutable::Hash`:
24
+ #
25
+ # Immutable::Hash.new([[:first_name, 'John'], [:last_name, 'Smith']])
26
+ #
27
+ # Key/value pairs can be added using {#put}. A new hash is returned and the
28
+ # existing one is left unchanged:
29
+ #
30
+ # hash = Immutable::Hash[a: 100, b: 200]
31
+ # hash.put(:c, 500) # => Immutable::Hash[:a => 100, :b => 200, :c => 500]
32
+ # hash # => Immutable::Hash[:a => 100, :b => 200]
33
+ #
34
+ # {#put} can also take a block, which is used to calculate the value to be
35
+ # stored.
36
+ #
37
+ # hash.put(:a) { |current| current + 200 } # => Immutable::Hash[:a => 300, :b => 200]
38
+ #
39
+ # Since it is immutable, all methods which you might expect to "modify" a
40
+ # `Immutable::Hash` actually return a new hash and leave the existing one
41
+ # unchanged. This means that the `hash[key] = value` syntax from RubyHash
42
+ # *cannot* be used with `Immutable::Hash`.
43
+ #
44
+ # Nested data structures can easily be updated using {#update_in}:
45
+ #
46
+ # hash = Immutable::Hash["a" => Immutable::Vector[Immutable::Hash["c" => 42]]]
47
+ # hash.update_in("a", 0, "c") { |value| value + 5 }
48
+ # # => Immutable::Hash["a" => Immutable::Hash["b" => Immutable::Hash["c" => 47]]]
49
+ #
50
+ # While an `Immutable::Hash` can iterate over its keys or values, it does not
51
+ # guarantee any specific iteration order (unlike RubyHash). Methods like
52
+ # {#flatten} do not guarantee the order of returned key/value pairs.
53
+ #
54
+ # Like RubyHash, an `Immutable::Hash` can have a default block which is used
55
+ # when looking up a key that does not exist. Unlike RubyHash, the default
56
+ # block will only be passed the missing key, without the hash itself:
57
+ #
58
+ # hash = Immutable::Hash.new { |missing_key| missing_key * 10 }
59
+ # hash[5] # => 50
60
+ class Hash
61
+ include Immutable::Enumerable
62
+
63
+ class << self
64
+ # Create a new `Hash` populated with the given key/value pairs.
65
+ #
66
+ # @example
67
+ # Immutable::Hash["A" => 1, "B" => 2] # => Immutable::Hash["A" => 1, "B" => 2]
68
+ # Immutable::Hash[["A", 1], ["B", 2]] # => Immutable::Hash["A" => 1, "B" => 2]
69
+ #
70
+ # @param pairs [::Enumerable] initial content of hash. An empty hash is returned if not provided.
71
+ # @return [Hash]
72
+ def [](pairs = nil)
73
+ (pairs.nil? || pairs.empty?) ? empty : new(pairs)
74
+ end
75
+
76
+ # Return an empty `Hash`. If used on a subclass, returns an empty instance
77
+ # of that class.
78
+ #
79
+ # @return [Hash]
80
+ def empty
81
+ @empty ||= self.new
82
+ end
83
+
84
+ # "Raw" allocation of a new `Hash`. Used internally to create a new
85
+ # instance quickly after obtaining a modified {Trie}.
86
+ #
87
+ # @return [Hash]
88
+ # @private
89
+ def alloc(trie = EmptyTrie, block = nil)
90
+ obj = allocate
91
+ obj.instance_variable_set(:@trie, trie)
92
+ obj.instance_variable_set(:@default, block)
93
+ obj.freeze
94
+ end
95
+ end
96
+
97
+ # @param pairs [::Enumerable] initial content of hash. An empty hash is returned if not provided.
98
+ # @yield [key] Optional _default block_ to be stored and used to calculate the default value of a missing key. It will not be yielded during this method. It will not be preserved when marshalling.
99
+ # @yieldparam key Key that was not present in the hash.
100
+ def initialize(pairs = nil, &block)
101
+ @trie = pairs ? Trie[pairs] : EmptyTrie
102
+ @default = block
103
+ freeze
104
+ end
105
+
106
+ # Return the default block if there is one. Otherwise, return `nil`.
107
+ #
108
+ # @return [Proc]
109
+ def default_proc
110
+ @default
111
+ end
112
+
113
+ # Return the number of key/value pairs in this `Hash`.
114
+ #
115
+ # @example
116
+ # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].size # => 3
117
+ #
118
+ # @return [Integer]
119
+ def size
120
+ @trie.size
121
+ end
122
+ alias :length :size
123
+
124
+ # Return `true` if this `Hash` contains no key/value pairs.
125
+ #
126
+ # @return [Boolean]
127
+ def empty?
128
+ @trie.empty?
129
+ end
130
+
131
+ # Return `true` if the given key object is present in this `Hash`. More precisely,
132
+ # return `true` if a key with the same `#hash` code, and which is also `#eql?`
133
+ # to the given key object is present.
134
+ #
135
+ # @example
136
+ # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].key?("B") # => true
137
+ #
138
+ # @param key [Object] The key to check for
139
+ # @return [Boolean]
140
+ def key?(key)
141
+ @trie.key?(key)
142
+ end
143
+ alias :has_key? :key?
144
+ alias :include? :key?
145
+ alias :member? :key?
146
+
147
+ # Return `true` if this `Hash` has one or more keys which map to the provided value.
148
+ #
149
+ # @example
150
+ # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].value?(2) # => true
151
+ #
152
+ # @param value [Object] The value to check for
153
+ # @return [Boolean]
154
+ def value?(value)
155
+ each { |k,v| return true if value == v }
156
+ false
157
+ end
158
+ alias :has_value? :value?
159
+
160
+ # Retrieve the value corresponding to the provided key object. If not found, and
161
+ # this `Hash` has a default block, the default block is called to provide the
162
+ # value. Otherwise, return `nil`.
163
+ #
164
+ # @example
165
+ # h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
166
+ # h["B"] # => 2
167
+ # h.get("B") # => 2
168
+ # h.get("Elephant") # => nil
169
+ #
170
+ # # Immutable Hash with a default proc:
171
+ # h = Immutable::Hash.new("A" => 1, "B" => 2, "C" => 3) { |key| key.size }
172
+ # h.get("B") # => 2
173
+ # h.get("Elephant") # => 8
174
+ #
175
+ # @param key [Object] The key to look up
176
+ # @return [Object]
177
+ def get(key)
178
+ entry = @trie.get(key)
179
+ if entry
180
+ entry[1]
181
+ elsif @default
182
+ @default.call(key)
183
+ end
184
+ end
185
+ alias :[] :get
186
+
187
+ # Retrieve the value corresponding to the given key object, or use the provided
188
+ # default value or block, or otherwise raise a `KeyError`.
189
+ #
190
+ # @overload fetch(key)
191
+ # Retrieve the value corresponding to the given key, or raise a `KeyError`
192
+ # if it is not found.
193
+ # @param key [Object] The key to look up
194
+ # @overload fetch(key) { |key| ... }
195
+ # Retrieve the value corresponding to the given key, or call the optional
196
+ # code block (with the missing key) and get its return value.
197
+ # @yield [key] The key which was not found
198
+ # @yieldreturn [Object] Object to return since the key was not found
199
+ # @param key [Object] The key to look up
200
+ # @overload fetch(key, default)
201
+ # Retrieve the value corresponding to the given key, or else return
202
+ # the provided `default` value.
203
+ # @param key [Object] The key to look up
204
+ # @param default [Object] Object to return if the key is not found
205
+ #
206
+ # @example
207
+ # h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
208
+ # h.fetch("B") # => 2
209
+ # h.fetch("Elephant") # => KeyError: key not found: "Elephant"
210
+ #
211
+ # # with a default value:
212
+ # h.fetch("B", 99) # => 2
213
+ # h.fetch("Elephant", 99) # => 99
214
+ #
215
+ # # with a block:
216
+ # h.fetch("B") { |key| key.size } # => 2
217
+ # h.fetch("Elephant") { |key| key.size } # => 8
218
+ #
219
+ # @return [Object]
220
+ def fetch(key, default = Undefined)
221
+ entry = @trie.get(key)
222
+ if entry
223
+ entry[1]
224
+ elsif block_given?
225
+ yield(key)
226
+ elsif default != Undefined
227
+ default
228
+ else
229
+ raise KeyError, "key not found: #{key.inspect}"
230
+ end
231
+ end
232
+
233
+ # Return a new `Hash` with the existing key/value associations, plus an association
234
+ # between the provided key and value. If an equivalent key is already present, its
235
+ # associated value will be replaced with the provided one.
236
+ #
237
+ # If the `value` argument is missing, but an optional code block is provided,
238
+ # it will be passed the existing value (or `nil` if there is none) and what it
239
+ # returns will replace the existing value. This is useful for "transforming"
240
+ # the value associated with a certain key.
241
+ #
242
+ # Avoid mutating objects which are used as keys. `String`s are an exception:
243
+ # unfrozen `String`s which are used as keys are internally duplicated and
244
+ # frozen. This matches RubyHash's behaviour.
245
+ #
246
+ # @example
247
+ # h = Immutable::Hash["A" => 1, "B" => 2]
248
+ # h.put("C", 3)
249
+ # # => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
250
+ # h.put("B") { |value| value * 10 }
251
+ # # => Immutable::Hash["A" => 1, "B" => 20]
252
+ #
253
+ # @param key [Object] The key to store
254
+ # @param value [Object] The value to associate it with
255
+ # @yield [value] The previously stored value, or `nil` if none.
256
+ # @yieldreturn [Object] The new value to store
257
+ # @return [Hash]
258
+ def put(key, value = yield(get(key)))
259
+ new_trie = @trie.put(key, value)
260
+ if new_trie.equal?(@trie)
261
+ self
262
+ else
263
+ self.class.alloc(new_trie, @default)
264
+ end
265
+ end
266
+
267
+ # Return a new `Hash` with a deeply nested value modified to the result of
268
+ # the given code block. When traversing the nested `Hash`es and `Vector`s,
269
+ # non-existing keys are created with empty `Hash` values.
270
+ #
271
+ # The code block receives the existing value of the deeply nested key (or
272
+ # `nil` if it doesn't exist). This is useful for "transforming" the value
273
+ # associated with a certain key.
274
+ #
275
+ # Note that the original `Hash` and sub-`Hash`es and sub-`Vector`s are left
276
+ # unmodified; new data structure copies are created along the path wherever
277
+ # needed.
278
+ #
279
+ # @example
280
+ # hash = Immutable::Hash["a" => Immutable::Hash["b" => Immutable::Hash["c" => 42]]]
281
+ # hash.update_in("a", "b", "c") { |value| value + 5 }
282
+ # # => Immutable::Hash["a" => Immutable::Hash["b" => Immutable::Hash["c" => 47]]]
283
+ #
284
+ # @param key_path [::Array<Object>] List of keys which form the path to the key to be modified
285
+ # @yield [value] The previously stored value
286
+ # @yieldreturn [Object] The new value to store
287
+ # @return [Hash]
288
+ def update_in(*key_path, &block)
289
+ if key_path.empty?
290
+ raise ArgumentError, "must have at least one key in path"
291
+ end
292
+ key = key_path[0]
293
+ if key_path.size == 1
294
+ new_value = block.call(get(key))
295
+ else
296
+ value = fetch(key, EmptyHash)
297
+ new_value = value.update_in(*key_path[1..-1], &block)
298
+ end
299
+ put(key, new_value)
300
+ end
301
+
302
+ # An alias for {#put} to match RubyHash's API. Does not support {#put}'s
303
+ # block form.
304
+ #
305
+ # @see #put
306
+ # @param key [Object] The key to store
307
+ # @param value [Object] The value to associate it with
308
+ # @return [Hash]
309
+ def store(key, value)
310
+ put(key, value)
311
+ end
312
+
313
+ # Return a new `Hash` with `key` removed. If `key` is not present, return
314
+ # `self`.
315
+ #
316
+ # @example
317
+ # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].delete("B")
318
+ # # => Immutable::Hash["A" => 1, "C" => 3]
319
+ #
320
+ # @param key [Object] The key to remove
321
+ # @return [Hash]
322
+ def delete(key)
323
+ derive_new_hash(@trie.delete(key))
324
+ end
325
+
326
+ # Call the block once for each key/value pair in this `Hash`, passing the key/value
327
+ # pair as parameters. No specific iteration order is guaranteed, though the order will
328
+ # be stable for any particular `Hash`.
329
+ #
330
+ # @example
331
+ # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].each { |k, v| puts "k=#{k} v=#{v}" }
332
+ #
333
+ # k=A v=1
334
+ # k=C v=3
335
+ # k=B v=2
336
+ # # => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
337
+ #
338
+ # @yield [key, value] Once for each key/value pair.
339
+ # @return [self]
340
+ def each(&block)
341
+ return to_enum if not block_given?
342
+ @trie.each(&block)
343
+ self
344
+ end
345
+ alias :each_pair :each
346
+
347
+ # Call the block once for each key/value pair in this `Hash`, passing the key/value
348
+ # pair as parameters. Iteration order will be the opposite of {#each}.
349
+ #
350
+ # @example
351
+ # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].reverse_each { |k, v| puts "k=#{k} v=#{v}" }
352
+ #
353
+ # k=B v=2
354
+ # k=C v=3
355
+ # k=A v=1
356
+ # # => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
357
+ #
358
+ # @yield [key, value] Once for each key/value pair.
359
+ # @return [self]
360
+ def reverse_each(&block)
361
+ return enum_for(:reverse_each) if not block_given?
362
+ @trie.reverse_each(&block)
363
+ self
364
+ end
365
+
366
+ # Call the block once for each key/value pair in this `Hash`, passing the key as a
367
+ # parameter. Ordering guarantees are the same as {#each}.
368
+ #
369
+ # @example
370
+ # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].each_key { |k| puts "k=#{k}" }
371
+ #
372
+ # k=A
373
+ # k=C
374
+ # k=B
375
+ # # => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
376
+ #
377
+ # @yield [key] Once for each key/value pair.
378
+ # @return [self]
379
+ def each_key
380
+ return enum_for(:each_key) if not block_given?
381
+ @trie.each { |k,v| yield k }
382
+ self
383
+ end
384
+
385
+ # Call the block once for each key/value pair in this `Hash`, passing the value as a
386
+ # parameter. Ordering guarantees are the same as {#each}.
387
+ #
388
+ # @example
389
+ # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].each_value { |v| puts "v=#{v}" }
390
+ #
391
+ # v=1
392
+ # v=3
393
+ # v=2
394
+ # # => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
395
+ #
396
+ # @yield [value] Once for each key/value pair.
397
+ # @return [self]
398
+ def each_value
399
+ return enum_for(:each_value) if not block_given?
400
+ @trie.each { |k,v| yield v }
401
+ self
402
+ end
403
+
404
+ # Call the block once for each key/value pair in this `Hash`, passing the key/value
405
+ # pair as parameters. The block should return a `[key, value]` array each time.
406
+ # All the returned `[key, value]` arrays will be gathered into a new `Hash`.
407
+ #
408
+ # @example
409
+ # h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
410
+ # h.map { |k, v| ["new-#{k}", v * v] }
411
+ # # => Hash["new-C" => 9, "new-B" => 4, "new-A" => 1]
412
+ #
413
+ # @yield [key, value] Once for each key/value pair.
414
+ # @return [Hash]
415
+ def map
416
+ return enum_for(:map) unless block_given?
417
+ return self if empty?
418
+ self.class.new(super, &@default)
419
+ end
420
+ alias :collect :map
421
+
422
+ # Return a new `Hash` with all the key/value pairs for which the block returns true.
423
+ #
424
+ # @example
425
+ # h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
426
+ # h.select { |k, v| v >= 2 }
427
+ # # => Immutable::Hash["B" => 2, "C" => 3]
428
+ #
429
+ # @yield [key, value] Once for each key/value pair.
430
+ # @yieldreturn Truthy if this pair should be present in the new `Hash`.
431
+ # @return [Hash]
432
+ def select(&block)
433
+ return enum_for(:select) unless block_given?
434
+ derive_new_hash(@trie.select(&block))
435
+ end
436
+ alias :find_all :select
437
+ alias :keep_if :select
438
+
439
+ # Yield `[key, value]` pairs until one is found for which the block returns true.
440
+ # Return that `[key, value]` pair. If the block never returns true, return `nil`.
441
+ #
442
+ # @example
443
+ # h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
444
+ # h.find { |k, v| v.even? }
445
+ # # => ["B", 2]
446
+ #
447
+ # @return [Array]
448
+ # @yield [key, value] At most once for each key/value pair, until the block returns `true`.
449
+ # @yieldreturn Truthy to halt iteration and return the yielded key/value pair.
450
+ def find
451
+ return enum_for(:find) unless block_given?
452
+ each { |entry| return entry if yield entry }
453
+ nil
454
+ end
455
+ alias :detect :find
456
+
457
+ # Return a new `Hash` containing all the key/value pairs from this `Hash` and
458
+ # `other`. If no block is provided, the value for entries with colliding keys
459
+ # will be that from `other`. Otherwise, the value for each duplicate key is
460
+ # determined by calling the block.
461
+ #
462
+ # `other` can be an `Immutable::Hash`, a built-in Ruby `Hash`, or any `Enumerable`
463
+ # object which yields `[key, value]` pairs.
464
+ #
465
+ # @example
466
+ # h1 = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
467
+ # h2 = Immutable::Hash["C" => 70, "D" => 80]
468
+ # h1.merge(h2)
469
+ # # => Immutable::Hash["C" => 70, "A" => 1, "D" => 80, "B" => 2]
470
+ # h1.merge(h2) { |key, v1, v2| v1 + v2 }
471
+ # # => Immutable::Hash["C" => 73, "A" => 1, "D" => 80, "B" => 2]
472
+ #
473
+ # @param other [::Enumerable] The collection to merge with
474
+ # @yieldparam key [Object] The key which was present in both collections
475
+ # @yieldparam my_value [Object] The associated value from this `Hash`
476
+ # @yieldparam other_value [Object] The associated value from the other collection
477
+ # @yieldreturn [Object] The value to associate this key with in the new `Hash`
478
+ # @return [Hash]
479
+ def merge(other)
480
+ trie = if block_given?
481
+ other.reduce(@trie) do |trie, (key, value)|
482
+ if entry = trie.get(key)
483
+ trie.put(key, yield(key, entry[1], value))
484
+ else
485
+ trie.put(key, value)
486
+ end
487
+ end
488
+ else
489
+ @trie.bulk_put(other)
490
+ end
491
+
492
+ derive_new_hash(trie)
493
+ end
494
+
495
+ # Retrieve the value corresponding to the given key object, or use the provided
496
+ # default value or block, or otherwise raise a `KeyError`.
497
+ #
498
+ # @overload fetch(key)
499
+ # Retrieve the value corresponding to the given key, or raise a `KeyError`
500
+ # if it is not found.
501
+ # @param key [Object] The key to look up
502
+ # @overload fetch(key) { |key| ... }
503
+
504
+ # Return a sorted {Vector} which contains all the `[key, value]` pairs in
505
+ # this `Hash` as two-element `Array`s.
506
+ #
507
+ # @overload sort
508
+ # Uses `#<=>` to determine sorted order.
509
+ # @overload sort { |(k1, v1), (k2, v2)| ... }
510
+ # Uses the block as a comparator to determine sorted order.
511
+ #
512
+ # @example
513
+ # h = Immutable::Hash["Dog" => 1, "Elephant" => 2, "Lion" => 3]
514
+ # h.sort { |(k1, v1), (k2, v2)| k1.size <=> k2.size }
515
+ # # => Immutable::Vector[["Dog", 1], ["Lion", 3], ["Elephant", 2]]
516
+ # @yield [(k1, v1), (k2, v2)] Any number of times with different pairs of key/value associations.
517
+ # @yieldreturn [Integer] Negative if the first pair should be sorted
518
+ # lower, positive if the latter pair, or 0 if equal.
519
+ #
520
+ # @see ::Enumerable#sort
521
+ #
522
+ # @return [Vector]
523
+ def sort
524
+ Vector.new(super)
525
+ end
526
+
527
+ # Return a {Vector} which contains all the `[key, value]` pairs in this `Hash`
528
+ # as two-element Arrays. The order which the pairs will appear in is determined by
529
+ # passing each pair to the code block to obtain a sort key object, and comparing
530
+ # the sort keys using `#<=>`.
531
+ #
532
+ # @see ::Enumerable#sort_by
533
+ #
534
+ # @example
535
+ # h = Immutable::Hash["Dog" => 1, "Elephant" => 2, "Lion" => 3]
536
+ # h.sort_by { |key, value| key.size }
537
+ # # => Immutable::Vector[["Dog", 1], ["Lion", 3], ["Elephant", 2]]
538
+ #
539
+ # @yield [key, value] Once for each key/value pair.
540
+ # @yieldreturn a sort key object for the yielded pair.
541
+ # @return [Vector]
542
+ def sort_by
543
+ Vector.new(super)
544
+ end
545
+
546
+ # Return a new `Hash` with the associations for all of the given `keys` removed.
547
+ #
548
+ # @example
549
+ # h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
550
+ # h.except("A", "C") # => Immutable::Hash["B" => 2]
551
+ #
552
+ # @param keys [Array] The keys to remove
553
+ # @return [Hash]
554
+ def except(*keys)
555
+ keys.reduce(self) { |hash, key| hash.delete(key) }
556
+ end
557
+
558
+ # Return a new `Hash` with only the associations for the `wanted` keys retained.
559
+ #
560
+ # @example
561
+ # h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
562
+ # h.slice("B", "C") # => Immutable::Hash["B" => 2, "C" => 3]
563
+ #
564
+ # @param wanted [::Enumerable] The keys to retain
565
+ # @return [Hash]
566
+ def slice(*wanted)
567
+ trie = Trie.new(0)
568
+ wanted.each { |key| trie.put!(key, get(key)) if key?(key) }
569
+ self.class.alloc(trie, @default)
570
+ end
571
+
572
+ # Return a {Vector} of the values which correspond to the `wanted` keys.
573
+ # If any of the `wanted` keys are not present in this `Hash`, they will be skipped.
574
+ #
575
+ # @example
576
+ # h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
577
+ # h.values_at("B", "A", "D") # => Immutable::Vector[2, 1]
578
+ #
579
+ # @param wanted [Array] The keys to retrieve
580
+ # @return [Vector]
581
+ def values_at(*wanted)
582
+ array = []
583
+ wanted.each { |key| array << get(key) if key?(key) }
584
+ Vector.new(array.freeze)
585
+ end
586
+
587
+ # Return a new {Set} containing the keys from this `Hash`.
588
+ #
589
+ # @example
590
+ # Immutable::Hash["A" => 1, "B" => 2, "C" => 3, "D" => 2].keys
591
+ # # => Immutable::Set["D", "C", "B", "A"]
592
+ #
593
+ # @return [Set]
594
+ def keys
595
+ Set.alloc(@trie)
596
+ end
597
+
598
+ # Return a new {Vector} populated with the values from this `Hash`.
599
+ #
600
+ # @example
601
+ # Immutable::Hash["A" => 1, "B" => 2, "C" => 3, "D" => 2].values
602
+ # # => Immutable::Vector[2, 3, 2, 1]
603
+ #
604
+ # @return [Vector]
605
+ def values
606
+ Vector.new(each_value.to_a.freeze)
607
+ end
608
+
609
+ # Return a new `Hash` created by using keys as values and values as keys.
610
+ # If there are multiple values which are equivalent (as determined by `#hash` and
611
+ # `#eql?`), only one out of each group of equivalent values will be
612
+ # retained. Which one specifically is undefined.
613
+ #
614
+ # @example
615
+ # Immutable::Hash["A" => 1, "B" => 2, "C" => 3, "D" => 2].invert
616
+ # # => Immutable::Hash[1 => "A", 3 => "C", 2 => "B"]
617
+ #
618
+ # @return [Hash]
619
+ def invert
620
+ pairs = []
621
+ each { |k,v| pairs << [v, k] }
622
+ self.class.new(pairs, &@default)
623
+ end
624
+
625
+ # Return a new {Vector} which is a one-dimensional flattening of this `Hash`.
626
+ # If `level` is 1, all the `[key, value]` pairs in the hash will be concatenated
627
+ # into one {Vector}. If `level` is greater than 1, keys or values which are
628
+ # themselves `Array`s or {Vector}s will be recursively flattened into the output
629
+ # {Vector}. The depth to which that flattening will be recursively applied is
630
+ # determined by `level`.
631
+ #
632
+ # As a special case, if `level` is 0, each `[key, value]` pair will be a
633
+ # separate element in the returned {Vector}.
634
+ #
635
+ # @example
636
+ # h = Immutable::Hash["A" => 1, "B" => [2, 3, 4]]
637
+ # h.flatten
638
+ # # => Immutable::Vector["A", 1, "B", [2, 3, 4]]
639
+ # h.flatten(2)
640
+ # # => Immutable::Vector["A", 1, "B", 2, 3, 4]
641
+ #
642
+ # @param level [Integer] The number of times to recursively flatten the `[key, value]` pairs in this `Hash`.
643
+ # @return [Vector]
644
+ def flatten(level = 1)
645
+ return Vector.new(self) if level == 0
646
+ array = []
647
+ each { |k,v| array << k; array << v }
648
+ array.flatten!(level-1) if level > 1
649
+ Vector.new(array.freeze)
650
+ end
651
+
652
+ # Searches through the `Hash`, comparing `obj` with each key (using `#==`).
653
+ # When a matching key is found, return the `[key, value]` pair as an array.
654
+ # Return `nil` if no match is found.
655
+ #
656
+ # @example
657
+ # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].assoc("B") # => ["B", 2]
658
+ #
659
+ # @param obj [Object] The key to search for (using #==)
660
+ # @return [Array]
661
+ def assoc(obj)
662
+ each { |entry| return entry if obj == entry[0] }
663
+ nil
664
+ end
665
+
666
+ # Searches through the `Hash`, comparing `obj` with each value (using `#==`).
667
+ # When a matching value is found, return the `[key, value]` pair as an array.
668
+ # Return `nil` if no match is found.
669
+ #
670
+ # @example
671
+ # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].rassoc(2) # => ["B", 2]
672
+ #
673
+ # @param obj [Object] The value to search for (using #==)
674
+ # @return [Array]
675
+ def rassoc(obj)
676
+ each { |entry| return entry if obj == entry[1] }
677
+ nil
678
+ end
679
+
680
+ # Searches through the `Hash`, comparing `value` with each value (using `#==`).
681
+ # When a matching value is found, return its associated key object.
682
+ # Return `nil` if no match is found.
683
+ #
684
+ # @example
685
+ # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].key(2) # => "B"
686
+ #
687
+ # @param value [Object] The value to search for (using #==)
688
+ # @return [Object]
689
+ def key(value)
690
+ each { |entry| return entry[0] if value == entry[1] }
691
+ nil
692
+ end
693
+
694
+ # Return a randomly chosen `[key, value]` pair from this `Hash`. If the hash is empty,
695
+ # return `nil`.
696
+ #
697
+ # @example
698
+ # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].sample
699
+ # # => ["C", 3]
700
+ #
701
+ # @return [Array]
702
+ def sample
703
+ @trie.at(rand(size))
704
+ end
705
+
706
+ # Return an empty `Hash` instance, of the same class as this one. Useful if you
707
+ # have multiple subclasses of `Hash` and want to treat them polymorphically.
708
+ # Maintains the default block, if there is one.
709
+ #
710
+ # @return [Hash]
711
+ def clear
712
+ if @default
713
+ self.class.alloc(EmptyTrie, @default)
714
+ else
715
+ self.class.empty
716
+ end
717
+ end
718
+
719
+ # Return true if `other` has the same type and contents as this `Hash`.
720
+ #
721
+ # @param other [Object] The collection to compare with
722
+ # @return [Boolean]
723
+ def eql?(other)
724
+ return true if other.equal?(self)
725
+ instance_of?(other.class) && @trie.eql?(other.instance_variable_get(:@trie))
726
+ end
727
+
728
+ # Return true if `other` has the same contents as this `Hash`. Will convert
729
+ # `other` to a Ruby `Hash` using `#to_hash` if necessary.
730
+ #
731
+ # @param other [Object] The object to compare with
732
+ # @return [Boolean]
733
+ def ==(other)
734
+ self.eql?(other) || (other.respond_to?(:to_hash) && to_hash.eql?(other.to_hash))
735
+ end
736
+
737
+ # See `Object#hash`.
738
+ # @return [Integer]
739
+ def hash
740
+ keys.to_a.sort.reduce(0) do |hash, key|
741
+ (hash << 32) - hash + key.hash + get(key).hash
742
+ end
743
+ end
744
+
745
+ # Return the contents of this `Hash` as a programmer-readable `String`. If all the
746
+ # keys and values are serializable as Ruby literal strings, the returned string can
747
+ # be passed to `eval` to reconstitute an equivalent `Hash`. The default
748
+ # block (if there is one) will be lost when doing this, however.
749
+ #
750
+ # @return [String]
751
+ def inspect
752
+ result = "#{self.class}["
753
+ i = 0
754
+ each do |key, val|
755
+ result << ', ' if i > 0
756
+ result << key.inspect << ' => ' << val.inspect
757
+ i += 1
758
+ end
759
+ result << "]"
760
+ end
761
+
762
+ # Return `self`. Since this is an immutable object duplicates are
763
+ # equivalent.
764
+ # @return [Hash]
765
+ def dup
766
+ self
767
+ end
768
+ alias :clone :dup
769
+
770
+ # Allows this `Hash` to be printed at the `pry` console, or using `pp` (from the
771
+ # Ruby standard library), in a way which takes the amount of horizontal space on
772
+ # the screen into account, and which indents nested structures to make them easier
773
+ # to read.
774
+ #
775
+ # @private
776
+ def pretty_print(pp)
777
+ pp.group(1, "#{self.class}[", "]") do
778
+ pp.breakable ''
779
+ pp.seplist(self, nil) do |key, val|
780
+ pp.group do
781
+ key.pretty_print(pp)
782
+ pp.text ' => '
783
+ pp.group(1) do
784
+ pp.breakable ''
785
+ val.pretty_print(pp)
786
+ end
787
+ end
788
+ end
789
+ end
790
+ end
791
+
792
+ # Convert this `Immutable::Hash` to an instance of Ruby's built-in `Hash`.
793
+ #
794
+ # @return [::Hash]
795
+ def to_hash
796
+ output = {}
797
+ each do |key, value|
798
+ output[key] = value
799
+ end
800
+ output
801
+ end
802
+ alias :to_h :to_hash
803
+
804
+ # @return [::Hash]
805
+ # @private
806
+ def marshal_dump
807
+ to_hash
808
+ end
809
+
810
+ # @private
811
+ def marshal_load(dictionary)
812
+ @trie = Trie[dictionary]
813
+ end
814
+
815
+ private
816
+
817
+ # Return a new `Hash` which is derived from this one, using a modified {Trie}.
818
+ # The new `Hash` will retain the existing default block, if there is one.
819
+ #
820
+ def derive_new_hash(trie)
821
+ if trie.equal?(@trie)
822
+ self
823
+ elsif trie.empty?
824
+ if @default
825
+ self.class.alloc(EmptyTrie, @default)
826
+ else
827
+ self.class.empty
828
+ end
829
+ else
830
+ self.class.alloc(trie, @default)
831
+ end
832
+ end
833
+ end
834
+
835
+ # The canonical empty `Hash`. Returned by `Hash[]` when
836
+ # invoked with no arguments; also returned by `Hash.empty`. Prefer using this
837
+ # one rather than creating many empty hashes using `Hash.new`.
838
+ #
839
+ # @private
840
+ EmptyHash = Immutable::Hash.empty
841
+ end