immutable-ruby 0.1.0 → 0.2.0

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.
@@ -1,941 +1,4 @@
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 ||= 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
- # @private
268
- # @raise NoMethodError
269
- def []=(*)
270
- raise NoMethodError, "Immutable::Hash doesn't support `[]='; use `put' instead"
271
- end
272
-
273
- # Return a new `Hash` with a deeply nested value modified to the result of
274
- # the given code block. When traversing the nested `Hash`es and `Vector`s,
275
- # non-existing keys are created with empty `Hash` values.
276
- #
277
- # The code block receives the existing value of the deeply nested key (or
278
- # `nil` if it doesn't exist). This is useful for "transforming" the value
279
- # associated with a certain key.
280
- #
281
- # Note that the original `Hash` and sub-`Hash`es and sub-`Vector`s are left
282
- # unmodified; new data structure copies are created along the path wherever
283
- # needed.
284
- #
285
- # @example
286
- # hash = Immutable::Hash["a" => Immutable::Hash["b" => Immutable::Hash["c" => 42]]]
287
- # hash.update_in("a", "b", "c") { |value| value + 5 }
288
- # # => Immutable::Hash["a" => Immutable::Hash["b" => Immutable::Hash["c" => 47]]]
289
- #
290
- # @param key_path [::Array<Object>] List of keys which form the path to the key to be modified
291
- # @yield [value] The previously stored value
292
- # @yieldreturn [Object] The new value to store
293
- # @return [Hash]
294
- def update_in(*key_path, &block)
295
- if key_path.empty?
296
- raise ArgumentError, 'must have at least one key in path'
297
- end
298
- key = key_path[0]
299
- if key_path.size == 1
300
- new_value = block.call(get(key))
301
- else
302
- value = fetch(key, EmptyHash)
303
- new_value = value.update_in(*key_path[1..-1], &block)
304
- end
305
- put(key, new_value)
306
- end
307
-
308
- # An alias for {#put} to match RubyHash's API. Does not support {#put}'s
309
- # block form.
310
- #
311
- # @see #put
312
- # @param key [Object] The key to store
313
- # @param value [Object] The value to associate it with
314
- # @return [Hash]
315
- def store(key, value)
316
- put(key, value)
317
- end
318
-
319
- # Return a new `Hash` with `key` removed. If `key` is not present, return
320
- # `self`.
321
- #
322
- # @example
323
- # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].delete("B")
324
- # # => Immutable::Hash["A" => 1, "C" => 3]
325
- #
326
- # @param key [Object] The key to remove
327
- # @return [Hash]
328
- def delete(key)
329
- derive_new_hash(@trie.delete(key))
330
- end
331
-
332
- # Call the block once for each key/value pair in this `Hash`, passing the key/value
333
- # pair as parameters. No specific iteration order is guaranteed, though the order will
334
- # be stable for any particular `Hash`.
335
- #
336
- # @example
337
- # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].each { |k, v| puts "k=#{k} v=#{v}" }
338
- #
339
- # k=A v=1
340
- # k=C v=3
341
- # k=B v=2
342
- # # => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
343
- #
344
- # @yield [key, value] Once for each key/value pair.
345
- # @return [self]
346
- def each(&block)
347
- return to_enum if not block_given?
348
- @trie.each(&block)
349
- self
350
- end
351
- alias each_pair each
352
-
353
- # Call the block once for each key/value pair in this `Hash`, passing the key/value
354
- # pair as parameters. Iteration order will be the opposite of {#each}.
355
- #
356
- # @example
357
- # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].reverse_each { |k, v| puts "k=#{k} v=#{v}" }
358
- #
359
- # k=B v=2
360
- # k=C v=3
361
- # k=A v=1
362
- # # => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
363
- #
364
- # @yield [key, value] Once for each key/value pair.
365
- # @return [self]
366
- def reverse_each(&block)
367
- return enum_for(:reverse_each) if not block_given?
368
- @trie.reverse_each(&block)
369
- self
370
- end
371
-
372
- # Call the block once for each key/value pair in this `Hash`, passing the key as a
373
- # parameter. Ordering guarantees are the same as {#each}.
374
- #
375
- # @example
376
- # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].each_key { |k| puts "k=#{k}" }
377
- #
378
- # k=A
379
- # k=C
380
- # k=B
381
- # # => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
382
- #
383
- # @yield [key] Once for each key/value pair.
384
- # @return [self]
385
- def each_key
386
- return enum_for(:each_key) if not block_given?
387
- @trie.each { |k,v| yield k }
388
- self
389
- end
390
-
391
- # Call the block once for each key/value pair in this `Hash`, passing the value as a
392
- # parameter. Ordering guarantees are the same as {#each}.
393
- #
394
- # @example
395
- # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].each_value { |v| puts "v=#{v}" }
396
- #
397
- # v=1
398
- # v=3
399
- # v=2
400
- # # => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
401
- #
402
- # @yield [value] Once for each key/value pair.
403
- # @return [self]
404
- def each_value
405
- return enum_for(:each_value) if not block_given?
406
- @trie.each { |k,v| yield v }
407
- self
408
- end
409
-
410
- # Call the block once for each key/value pair in this `Hash`, passing the key/value
411
- # pair as parameters. The block should return a `[key, value]` array each time.
412
- # All the returned `[key, value]` arrays will be gathered into a new `Hash`.
413
- #
414
- # @example
415
- # h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
416
- # h.map { |k, v| ["new-#{k}", v * v] }
417
- # # => Hash["new-C" => 9, "new-B" => 4, "new-A" => 1]
418
- #
419
- # @yield [key, value] Once for each key/value pair.
420
- # @return [Hash]
421
- def map
422
- return enum_for(:map) unless block_given?
423
- return self if empty?
424
- self.class.new(super, &@default)
425
- end
426
- alias collect map
427
-
428
- # Return a new `Hash` with all the key/value pairs for which the block returns true.
429
- #
430
- # @example
431
- # h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
432
- # h.select { |k, v| v >= 2 }
433
- # # => Immutable::Hash["B" => 2, "C" => 3]
434
- #
435
- # @yield [key, value] Once for each key/value pair.
436
- # @yieldreturn Truthy if this pair should be present in the new `Hash`.
437
- # @return [Hash]
438
- def select(&block)
439
- return enum_for(:select) unless block_given?
440
- derive_new_hash(@trie.select(&block))
441
- end
442
- alias find_all select
443
- alias keep_if select
444
-
445
- # Yield `[key, value]` pairs until one is found for which the block returns true.
446
- # Return that `[key, value]` pair. If the block never returns true, return `nil`.
447
- #
448
- # @example
449
- # h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
450
- # h.find { |k, v| v.even? }
451
- # # => ["B", 2]
452
- #
453
- # @return [Array]
454
- # @yield [key, value] At most once for each key/value pair, until the block returns `true`.
455
- # @yieldreturn Truthy to halt iteration and return the yielded key/value pair.
456
- def find
457
- return enum_for(:find) unless block_given?
458
- each { |entry| return entry if yield entry }
459
- nil
460
- end
461
- alias detect find
462
-
463
- # Return a new `Hash` containing all the key/value pairs from this `Hash` and
464
- # `other`. If no block is provided, the value for entries with colliding keys
465
- # will be that from `other`. Otherwise, the value for each duplicate key is
466
- # determined by calling the block.
467
- #
468
- # `other` can be an `Immutable::Hash`, a built-in Ruby `Hash`, or any `Enumerable`
469
- # object which yields `[key, value]` pairs.
470
- #
471
- # @example
472
- # h1 = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
473
- # h2 = Immutable::Hash["C" => 70, "D" => 80]
474
- # h1.merge(h2)
475
- # # => Immutable::Hash["C" => 70, "A" => 1, "D" => 80, "B" => 2]
476
- # h1.merge(h2) { |key, v1, v2| v1 + v2 }
477
- # # => Immutable::Hash["C" => 73, "A" => 1, "D" => 80, "B" => 2]
478
- #
479
- # @param other [::Enumerable] The collection to merge with
480
- # @yieldparam key [Object] The key which was present in both collections
481
- # @yieldparam my_value [Object] The associated value from this `Hash`
482
- # @yieldparam other_value [Object] The associated value from the other collection
483
- # @yieldreturn [Object] The value to associate this key with in the new `Hash`
484
- # @return [Hash]
485
- def merge(other)
486
- trie = if block_given?
487
- other.reduce(@trie) do |trie, (key, value)|
488
- if (entry = trie.get(key))
489
- trie.put(key, yield(key, entry[1], value))
490
- else
491
- trie.put(key, value)
492
- end
493
- end
494
- else
495
- @trie.bulk_put(other)
496
- end
497
-
498
- derive_new_hash(trie)
499
- end
500
-
501
- # Retrieve the value corresponding to the given key object, or use the provided
502
- # default value or block, or otherwise raise a `KeyError`.
503
- #
504
- # @overload fetch(key)
505
- # Retrieve the value corresponding to the given key, or raise a `KeyError`
506
- # if it is not found.
507
- # @param key [Object] The key to look up
508
- # @overload fetch(key) { |key| ... }
509
-
510
- # Return a sorted {Vector} which contains all the `[key, value]` pairs in
511
- # this `Hash` as two-element `Array`s.
512
- #
513
- # @overload sort
514
- # Uses `#<=>` to determine sorted order.
515
- # @overload sort { |(k1, v1), (k2, v2)| ... }
516
- # Uses the block as a comparator to determine sorted order.
517
- #
518
- # @example
519
- # h = Immutable::Hash["Dog" => 1, "Elephant" => 2, "Lion" => 3]
520
- # h.sort { |(k1, v1), (k2, v2)| k1.size <=> k2.size }
521
- # # => Immutable::Vector[["Dog", 1], ["Lion", 3], ["Elephant", 2]]
522
- # @yield [(k1, v1), (k2, v2)] Any number of times with different pairs of key/value associations.
523
- # @yieldreturn [Integer] Negative if the first pair should be sorted
524
- # lower, positive if the latter pair, or 0 if equal.
525
- #
526
- # @see ::Enumerable#sort
527
- #
528
- # @return [Vector]
529
- def sort
530
- Vector.new(super)
531
- end
532
-
533
- # Return a {Vector} which contains all the `[key, value]` pairs in this `Hash`
534
- # as two-element Arrays. The order which the pairs will appear in is determined by
535
- # passing each pair to the code block to obtain a sort key object, and comparing
536
- # the sort keys using `#<=>`.
537
- #
538
- # @see ::Enumerable#sort_by
539
- #
540
- # @example
541
- # h = Immutable::Hash["Dog" => 1, "Elephant" => 2, "Lion" => 3]
542
- # h.sort_by { |key, value| key.size }
543
- # # => Immutable::Vector[["Dog", 1], ["Lion", 3], ["Elephant", 2]]
544
- #
545
- # @yield [key, value] Once for each key/value pair.
546
- # @yieldreturn a sort key object for the yielded pair.
547
- # @return [Vector]
548
- def sort_by
549
- Vector.new(super)
550
- end
551
-
552
- # Return a new `Hash` with the associations for all of the given `keys` removed.
553
- #
554
- # @example
555
- # h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
556
- # h.except("A", "C") # => Immutable::Hash["B" => 2]
557
- #
558
- # @param keys [Array] The keys to remove
559
- # @return [Hash]
560
- def except(*keys)
561
- keys.reduce(self) { |hash, key| hash.delete(key) }
562
- end
563
-
564
- # Return a new `Hash` with only the associations for the `wanted` keys retained.
565
- #
566
- # @example
567
- # h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
568
- # h.slice("B", "C") # => Immutable::Hash["B" => 2, "C" => 3]
569
- #
570
- # @param wanted [::Enumerable] The keys to retain
571
- # @return [Hash]
572
- def slice(*wanted)
573
- trie = Trie.new(0)
574
- wanted.each { |key| trie.put!(key, get(key)) if key?(key) }
575
- self.class.alloc(trie, @default)
576
- end
577
-
578
- # Return a {Vector} of the values which correspond to the `wanted` keys.
579
- # If any of the `wanted` keys are not present in this `Hash`, `nil` will be
580
- # placed instead, or the result of the default proc (if one is defined),
581
- # similar to the behavior of {#get}.
582
- #
583
- # @example
584
- # h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
585
- # h.values_at("B", "A", "D") # => Immutable::Vector[2, 1, nil]
586
- #
587
- # @param wanted [Array] The keys to retrieve
588
- # @return [Vector]
589
- def values_at(*wanted)
590
- Vector.new(wanted.map { |key| get(key) }.freeze)
591
- end
592
-
593
- # Return a {Vector} of the values which correspond to the `wanted` keys.
594
- # If any of the `wanted` keys are not present in this `Hash`, raise `KeyError`
595
- # exception.
596
- #
597
- # @example
598
- # h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
599
- # h.fetch_values("C", "A") # => Immutable::Vector[3, 1]
600
- # h.fetch_values("C", "Z") # => KeyError: key not found: "Z"
601
- #
602
- # @param wanted [Array] The keys to retrieve
603
- # @return [Vector]
604
- def fetch_values(*wanted)
605
- array = wanted.map { |key| fetch(key) }
606
- Vector.new(array.freeze)
607
- end
608
-
609
- # Return the value of successively indexing into a nested collection.
610
- # If any of the keys is not present, return `nil`.
611
- #
612
- # @example
613
- # h = Immutable::Hash[a: 9, b: Immutable::Hash[c: 'a', d: 4], e: nil]
614
- # h.dig(:b, :c) # => "a"
615
- # h.dig(:b, :f) # => nil
616
- #
617
- # @return [Object]
618
- def dig(key, *rest)
619
- value = self[key]
620
- if rest.empty? || value.nil?
621
- value
622
- else
623
- value.dig(*rest)
624
- end
625
- end
626
-
627
- # Return a new {Set} containing the keys from this `Hash`.
628
- #
629
- # @example
630
- # Immutable::Hash["A" => 1, "B" => 2, "C" => 3, "D" => 2].keys
631
- # # => Immutable::Set["D", "C", "B", "A"]
632
- #
633
- # @return [Set]
634
- def keys
635
- Set.alloc(@trie)
636
- end
637
-
638
- # Return a new {Vector} populated with the values from this `Hash`.
639
- #
640
- # @example
641
- # Immutable::Hash["A" => 1, "B" => 2, "C" => 3, "D" => 2].values
642
- # # => Immutable::Vector[2, 3, 2, 1]
643
- #
644
- # @return [Vector]
645
- def values
646
- Vector.new(each_value.to_a.freeze)
647
- end
648
-
649
- # Return a new `Hash` created by using keys as values and values as keys.
650
- # If there are multiple values which are equivalent (as determined by `#hash` and
651
- # `#eql?`), only one out of each group of equivalent values will be
652
- # retained. Which one specifically is undefined.
653
- #
654
- # @example
655
- # Immutable::Hash["A" => 1, "B" => 2, "C" => 3, "D" => 2].invert
656
- # # => Immutable::Hash[1 => "A", 3 => "C", 2 => "B"]
657
- #
658
- # @return [Hash]
659
- def invert
660
- pairs = []
661
- each { |k,v| pairs << [v, k] }
662
- self.class.new(pairs, &@default)
663
- end
664
-
665
- # Return a new {Vector} which is a one-dimensional flattening of this `Hash`.
666
- # If `level` is 1, all the `[key, value]` pairs in the hash will be concatenated
667
- # into one {Vector}. If `level` is greater than 1, keys or values which are
668
- # themselves `Array`s or {Vector}s will be recursively flattened into the output
669
- # {Vector}. The depth to which that flattening will be recursively applied is
670
- # determined by `level`.
671
- #
672
- # As a special case, if `level` is 0, each `[key, value]` pair will be a
673
- # separate element in the returned {Vector}.
674
- #
675
- # @example
676
- # h = Immutable::Hash["A" => 1, "B" => [2, 3, 4]]
677
- # h.flatten
678
- # # => Immutable::Vector["A", 1, "B", [2, 3, 4]]
679
- # h.flatten(2)
680
- # # => Immutable::Vector["A", 1, "B", 2, 3, 4]
681
- #
682
- # @param level [Integer] The number of times to recursively flatten the `[key, value]` pairs in this `Hash`.
683
- # @return [Vector]
684
- def flatten(level = 1)
685
- return Vector.new(self) if level == 0
686
- array = []
687
- each { |k,v| array << k; array << v }
688
- array.flatten!(level-1) if level > 1
689
- Vector.new(array.freeze)
690
- end
691
-
692
- # Searches through the `Hash`, comparing `obj` with each key (using `#==`).
693
- # When a matching key is found, return the `[key, value]` pair as an array.
694
- # Return `nil` if no match is found.
695
- #
696
- # @example
697
- # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].assoc("B") # => ["B", 2]
698
- #
699
- # @param obj [Object] The key to search for (using #==)
700
- # @return [Array]
701
- def assoc(obj)
702
- each { |entry| return entry if obj == entry[0] }
703
- nil
704
- end
705
-
706
- # Searches through the `Hash`, comparing `obj` with each value (using `#==`).
707
- # When a matching value is found, return the `[key, value]` pair as an array.
708
- # Return `nil` if no match is found.
709
- #
710
- # @example
711
- # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].rassoc(2) # => ["B", 2]
712
- #
713
- # @param obj [Object] The value to search for (using #==)
714
- # @return [Array]
715
- def rassoc(obj)
716
- each { |entry| return entry if obj == entry[1] }
717
- nil
718
- end
719
-
720
- # Searches through the `Hash`, comparing `value` with each value (using `#==`).
721
- # When a matching value is found, return its associated key object.
722
- # Return `nil` if no match is found.
723
- #
724
- # @example
725
- # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].key(2) # => "B"
726
- #
727
- # @param value [Object] The value to search for (using #==)
728
- # @return [Object]
729
- def key(value)
730
- each { |entry| return entry[0] if value == entry[1] }
731
- nil
732
- end
733
-
734
- # Return a randomly chosen `[key, value]` pair from this `Hash`. If the hash is empty,
735
- # return `nil`.
736
- #
737
- # @example
738
- # Immutable::Hash["A" => 1, "B" => 2, "C" => 3].sample
739
- # # => ["C", 3]
740
- #
741
- # @return [Array]
742
- def sample
743
- @trie.at(rand(size))
744
- end
745
-
746
- # Return an empty `Hash` instance, of the same class as this one. Useful if you
747
- # have multiple subclasses of `Hash` and want to treat them polymorphically.
748
- # Maintains the default block, if there is one.
749
- #
750
- # @return [Hash]
751
- def clear
752
- if @default
753
- self.class.alloc(EmptyTrie, @default)
754
- else
755
- self.class.empty
756
- end
757
- end
758
-
759
- # Return true if `other` has the same type and contents as this `Hash`.
760
- #
761
- # @param other [Object] The collection to compare with
762
- # @return [Boolean]
763
- def eql?(other)
764
- return true if other.equal?(self)
765
- instance_of?(other.class) && @trie.eql?(other.instance_variable_get(:@trie))
766
- end
767
-
768
- # Return true if `other` has the same contents as this `Hash`. Will convert
769
- # `other` to a Ruby `Hash` using `#to_hash` if necessary.
770
- #
771
- # @param other [Object] The object to compare with
772
- # @return [Boolean]
773
- def ==(other)
774
- eql?(other) || (other.respond_to?(:to_hash) && to_hash == other.to_hash)
775
- end
776
-
777
- # Return true if this `Hash` is a proper superset of `other`, which means
778
- # all `other`'s keys are contained in this `Hash` with identical
779
- # values, and the two hashes are not identical.
780
- #
781
- # @param other [Immutable::Hash] The object to compare with
782
- # @return [Boolean]
783
- def >(other)
784
- self != other && self >= other
785
- end
786
-
787
- # Return true if this `Hash` is a superset of `other`, which means all
788
- # `other`'s keys are contained in this `Hash` with identical values.
789
- #
790
- # @param other [Immutable::Hash] The object to compare with
791
- # @return [Boolean]
792
- def >=(other)
793
- other.each do |key, value|
794
- if self[key] != value
795
- return false
796
- end
797
- end
798
- true
799
- end
800
-
801
- # Return true if this `Hash` is a proper subset of `other`, which means all
802
- # its keys are contained in `other` with the identical values, and the two
803
- # hashes are not identical.
804
- #
805
- # @param other [Immutable::Hash] The object to compare with
806
- # @return [Boolean]
807
- def <(other)
808
- other > self
809
- end
810
-
811
- # Return true if this `Hash` is a subset of `other`, which means all its
812
- # keys are contained in `other` with the identical values, and the two
813
- # hashes are not identical.
814
- #
815
- # @param other [Immutable::Hash] The object to compare with
816
- # @return [Boolean]
817
- def <=(other)
818
- other >= self
819
- end
820
-
821
- # See `Object#hash`.
822
- # @return [Integer]
823
- def hash
824
- keys.to_a.sort.reduce(0) do |hash, key|
825
- (hash << 32) - hash + key.hash + get(key).hash
826
- end
827
- end
828
-
829
- # Return the contents of this `Hash` as a programmer-readable `String`. If all the
830
- # keys and values are serializable as Ruby literal strings, the returned string can
831
- # be passed to `eval` to reconstitute an equivalent `Hash`. The default
832
- # block (if there is one) will be lost when doing this, however.
833
- #
834
- # @return [String]
835
- def inspect
836
- result = "#{self.class}["
837
- i = 0
838
- each do |key, val|
839
- result << ', ' if i > 0
840
- result << key.inspect << ' => ' << val.inspect
841
- i += 1
842
- end
843
- result << ']'
844
- end
845
-
846
- # Return `self`. Since this is an immutable object duplicates are
847
- # equivalent.
848
- # @return [Hash]
849
- def dup
850
- self
851
- end
852
- alias clone dup
853
-
854
- # Allows this `Hash` to be printed at the `pry` console, or using `pp` (from the
855
- # Ruby standard library), in a way which takes the amount of horizontal space on
856
- # the screen into account, and which indents nested structures to make them easier
857
- # to read.
858
- #
859
- # @private
860
- def pretty_print(pp)
861
- pp.group(1, "#{self.class}[", ']') do
862
- pp.breakable ''
863
- pp.seplist(self, nil) do |key, val|
864
- pp.group do
865
- key.pretty_print(pp)
866
- pp.text ' => '
867
- pp.group(1) do
868
- pp.breakable ''
869
- val.pretty_print(pp)
870
- end
871
- end
872
- end
873
- end
874
- end
875
-
876
- # Convert this `Immutable::Hash` to an instance of Ruby's built-in `Hash`.
877
- #
878
- # @return [::Hash]
879
- def to_hash
880
- output = {}
881
- each do |key, value|
882
- output[key] = value
883
- end
884
- output
885
- end
886
- alias to_h to_hash
887
-
888
- # Return a `Proc` which accepts a key as an argument and returns the value.
889
- # The `Proc` behaves like {#get} (when the key is missing, it returns nil or
890
- # the result of the default proc).
891
- #
892
- # @example
893
- # h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
894
- # h.to_proc.call("B")
895
- # # => 2
896
- # ["A", "C", "X"].map(&h) # The & is short for .to_proc in Ruby
897
- # # => [1, 3, nil]
898
- #
899
- # @return [Proc]
900
- def to_proc
901
- lambda { |key| get(key) }
902
- end
903
-
904
- # @return [::Hash]
905
- # @private
906
- def marshal_dump
907
- to_hash
908
- end
909
-
910
- # @private
911
- def marshal_load(dictionary)
912
- @trie = Trie[dictionary]
913
- end
914
-
915
- private
916
-
917
- # Return a new `Hash` which is derived from this one, using a modified {Trie}.
918
- # The new `Hash` will retain the existing default block, if there is one.
919
- #
920
- def derive_new_hash(trie)
921
- if trie.equal?(@trie)
922
- self
923
- elsif trie.empty?
924
- if @default
925
- self.class.alloc(EmptyTrie, @default)
926
- else
927
- self.class.empty
928
- end
929
- else
930
- self.class.alloc(trie, @default)
931
- end
932
- end
933
- end
934
-
935
- # The canonical empty `Hash`. Returned by `Hash[]` when
936
- # invoked with no arguments; also returned by `Hash.empty`. Prefer using this
937
- # one rather than creating many empty hashes using `Hash.new`.
938
- #
939
- # @private
940
- EmptyHash = Immutable::Hash.empty
941
- end
1
+ # Definition of Immutable::Hash is in a separate file to avoid circular
2
+ # dependency warnings caused by dependencies between Hash ↔ Vector and
3
+ # Hash ↔ Set
4
+ require 'immutable/_core'