immutable-ruby 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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'