Class: Immutable::Hash

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/immutable/hash.rb

Overview

An ‘Immutable::Hash` maps a set of unique keys to corresponding values, much like a dictionary maps from words to definitions. Given a key, it can store and retrieve an associated value in constant time. If an existing key is stored again, the new value will replace the old. It behaves much like Ruby’s built-in Hash, which we will call RubyHash for clarity. Like RubyHash, two keys that are ‘#eql?` to each other and have the same `#hash` are considered identical in an `Immutable::Hash`.

An ‘Immutable::Hash` can be created in a couple of ways:

Immutable::Hash.new(font_size: 10, font_family: 'Arial')
Immutable::Hash[first_name: 'John', last_name: 'Smith']

Any ‘Enumerable` object which yields two-element `[key, value]` arrays can be used to initialize an `Immutable::Hash`:

Immutable::Hash.new([[:first_name, 'John'], [:last_name, 'Smith']])

Key/value pairs can be added using #put. A new hash is returned and the existing one is left unchanged:

hash = Immutable::Hash[a: 100, b: 200]
hash.put(:c, 500) # => Immutable::Hash[:a => 100, :b => 200, :c => 500]
hash              # => Immutable::Hash[:a => 100, :b => 200]

#put can also take a block, which is used to calculate the value to be stored.

hash.put(:a) { |current| current + 200 } # => Immutable::Hash[:a => 300, :b => 200]

Since it is immutable, all methods which you might expect to “modify” a ‘Immutable::Hash` actually return a new hash and leave the existing one unchanged. This means that the `hash = value` syntax from RubyHash cannot be used with `Immutable::Hash`.

Nested data structures can easily be updated using #update_in:

hash = Immutable::Hash["a" => Immutable::Vector[Immutable::Hash["c" => 42]]]
hash.update_in("a", 0, "c") { |value| value + 5 }
# => Immutable::Hash["a" => Immutable::Hash["b" => Immutable::Hash["c" => 47]]]

While an ‘Immutable::Hash` can iterate over its keys or values, it does not guarantee any specific iteration order (unlike RubyHash). Methods like #flatten do not guarantee the order of returned key/value pairs.

Like RubyHash, an ‘Immutable::Hash` can have a default block which is used when looking up a key that does not exist. Unlike RubyHash, the default block will only be passed the missing key, without the hash itself:

hash = Immutable::Hash.new { |missing_key| missing_key * 10 }
hash[5] # => 50

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Enumerable

#<=>, #compact, #each_index, #grep, #group_by, #join, #partition, #product, #reject, #sum, #to_set

Methods included from Enumerable

#to_list

Constructor Details

#initialize(pairs = nil) {|key| ... } ⇒ Hash

Returns a new instance of Hash.

Parameters:

  • pairs (::Enumerable) (defaults to: nil)

    initial content of hash. An empty hash is returned if not provided.

Yields:

  • (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.

Yield Parameters:

  • key

    Key that was not present in the hash.



100
101
102
103
104
# File 'lib/immutable/hash.rb', line 100

def initialize(pairs = nil, &block)
  @trie    = pairs ? Trie[pairs] : EmptyTrie
  @default = block
  freeze
end

Class Method Details

.[](pairs = nil) ⇒ Hash

Create a new ‘Hash` populated with the given key/value pairs.

Examples:

Immutable::Hash["A" => 1, "B" => 2] # => Immutable::Hash["A" => 1, "B" => 2]
Immutable::Hash[["A", 1], ["B", 2]] # => Immutable::Hash["A" => 1, "B" => 2]

Parameters:

  • pairs (::Enumerable) (defaults to: nil)

    initial content of hash. An empty hash is returned if not provided.

Returns:



72
73
74
# File 'lib/immutable/hash.rb', line 72

def [](pairs = nil)
  (pairs.nil? || pairs.empty?) ? empty : new(pairs)
end

.alloc(trie = EmptyTrie, block = nil) ⇒ Hash

“Raw” allocation of a new ‘Hash`. Used internally to create a new instance quickly after obtaining a modified Trie.

Returns:



89
90
91
92
93
94
# File 'lib/immutable/hash.rb', line 89

def alloc(trie = EmptyTrie, block = nil)
  obj = allocate
  obj.instance_variable_set(:@trie, trie)
  obj.instance_variable_set(:@default, block)
  obj.freeze
end

.emptyHash

Return an empty ‘Hash`. If used on a subclass, returns an empty instance of that class.

Returns:



80
81
82
# File 'lib/immutable/hash.rb', line 80

def empty
  @empty ||= self.new
end

Instance Method Details

#==(other) ⇒ Boolean

Return true if ‘other` has the same contents as this `Hash`. Will convert `other` to a Ruby `Hash` using `#to_hash` if necessary.

Parameters:

  • other (Object)

    The object to compare with

Returns:

  • (Boolean)


733
734
735
# File 'lib/immutable/hash.rb', line 733

def ==(other)
  self.eql?(other) || (other.respond_to?(:to_hash) && to_hash.eql?(other.to_hash))
end

#assoc(obj) ⇒ Array

Searches through the ‘Hash`, comparing `obj` with each key (using `#==`). When a matching key is found, return the `[key, value]` pair as an array. Return `nil` if no match is found.

Examples:

Immutable::Hash["A" => 1, "B" => 2, "C" => 3].assoc("B")  # => ["B", 2]

Parameters:

  • obj (Object)

    The key to search for (using #==)

Returns:

  • (Array)


661
662
663
664
# File 'lib/immutable/hash.rb', line 661

def assoc(obj)
  each { |entry| return entry if obj == entry[0] }
  nil
end

#clearHash

Return an empty ‘Hash` instance, of the same class as this one. Useful if you have multiple subclasses of `Hash` and want to treat them polymorphically. Maintains the default block, if there is one.

Returns:



711
712
713
714
715
716
717
# File 'lib/immutable/hash.rb', line 711

def clear
  if @default
    self.class.alloc(EmptyTrie, @default)
  else
    self.class.empty
  end
end

#default_procProc

Return the default block if there is one. Otherwise, return ‘nil`.

Returns:

  • (Proc)


109
110
111
# File 'lib/immutable/hash.rb', line 109

def default_proc
  @default
end

#delete(key) ⇒ Hash

Return a new ‘Hash` with `key` removed. If `key` is not present, return `self`.

Examples:

Immutable::Hash["A" => 1, "B" => 2, "C" => 3].delete("B")
# => Immutable::Hash["A" => 1, "C" => 3]

Parameters:

  • key (Object)

    The key to remove

Returns:



322
323
324
# File 'lib/immutable/hash.rb', line 322

def delete(key)
  derive_new_hash(@trie.delete(key))
end

#dupHash Also known as: clone

Return ‘self`. Since this is an immutable object duplicates are equivalent.

Returns:



765
766
767
# File 'lib/immutable/hash.rb', line 765

def dup
  self
end

#each {|key, value| ... } ⇒ self Also known as: each_pair

Call the block once for each key/value pair in this ‘Hash`, passing the key/value pair as parameters. No specific iteration order is guaranteed, though the order will be stable for any particular `Hash`.

Examples:

Immutable::Hash["A" => 1, "B" => 2, "C" => 3].each { |k, v| puts "k=#{k} v=#{v}" }

k=A v=1
k=C v=3
k=B v=2
# => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]

Yields:

  • (key, value)

    Once for each key/value pair.

Returns:

  • (self)


340
341
342
343
344
# File 'lib/immutable/hash.rb', line 340

def each(&block)
  return to_enum if not block_given?
  @trie.each(&block)
  self
end

#each_key {|key| ... } ⇒ self

Call the block once for each key/value pair in this ‘Hash`, passing the key as a parameter. Ordering guarantees are the same as #each.

Examples:

Immutable::Hash["A" => 1, "B" => 2, "C" => 3].each_key { |k| puts "k=#{k}" }

k=A
k=C
k=B
# => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]

Yields:

  • (key)

    Once for each key/value pair.

Returns:

  • (self)


379
380
381
382
383
# File 'lib/immutable/hash.rb', line 379

def each_key
  return enum_for(:each_key) if not block_given?
  @trie.each { |k,v| yield k }
  self
end

#each_value {|value| ... } ⇒ self

Call the block once for each key/value pair in this ‘Hash`, passing the value as a parameter. Ordering guarantees are the same as #each.

Examples:

Immutable::Hash["A" => 1, "B" => 2, "C" => 3].each_value { |v| puts "v=#{v}" }

v=1
v=3
v=2
# => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]

Yields:

  • (value)

    Once for each key/value pair.

Returns:

  • (self)


398
399
400
401
402
# File 'lib/immutable/hash.rb', line 398

def each_value
  return enum_for(:each_value) if not block_given?
  @trie.each { |k,v| yield v }
  self
end

#empty?Boolean

Return ‘true` if this `Hash` contains no key/value pairs.

Returns:

  • (Boolean)


127
128
129
# File 'lib/immutable/hash.rb', line 127

def empty?
  @trie.empty?
end

#eql?(other) ⇒ Boolean

Return true if ‘other` has the same type and contents as this `Hash`.

Parameters:

  • other (Object)

    The collection to compare with

Returns:

  • (Boolean)


723
724
725
726
# File 'lib/immutable/hash.rb', line 723

def eql?(other)
  return true if other.equal?(self)
  instance_of?(other.class) && @trie.eql?(other.instance_variable_get(:@trie))
end

#except(*keys) ⇒ Hash

Return a new ‘Hash` with the associations for all of the given `keys` removed.

Examples:

h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
h.except("A", "C")  # => Immutable::Hash["B" => 2]

Parameters:

  • keys (Array)

    The keys to remove

Returns:



554
555
556
# File 'lib/immutable/hash.rb', line 554

def except(*keys)
  keys.reduce(self) { |hash, key| hash.delete(key) }
end

#fetch(key) ⇒ Object #fetch(key) {|key| ... } ⇒ Object #fetch(key, default) ⇒ Object

Retrieve the value corresponding to the given key object, or use the provided default value or block, or otherwise raise a ‘KeyError`.

Examples:

h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
h.fetch("B")         # => 2
h.fetch("Elephant")  # => KeyError: key not found: "Elephant"

# with a default value:
h.fetch("B", 99)         # => 2
h.fetch("Elephant", 99)  # => 99

# with a block:
h.fetch("B") { |key| key.size }         # => 2
h.fetch("Elephant") { |key| key.size }  # => 8

Overloads:

  • #fetch(key) ⇒ Object

    Retrieve the value corresponding to the given key, or raise a ‘KeyError` if it is not found.

    Parameters:

    • key (Object)

      The key to look up

  • #fetch(key) {|key| ... } ⇒ Object

    Retrieve the value corresponding to the given key, or call the optional code block (with the missing key) and get its return value.

    Parameters:

    • key (Object)

      The key to look up

    Yields:

    • (key)

      The key which was not found

    Yield Returns:

    • (Object)

      Object to return since the key was not found

  • #fetch(key, default) ⇒ Object

    Retrieve the value corresponding to the given key, or else return the provided ‘default` value.

    Parameters:

    • key (Object)

      The key to look up

    • default (Object)

      Object to return if the key is not found

Returns:

  • (Object)


220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/immutable/hash.rb', line 220

def fetch(key, default = Undefined)
  entry = @trie.get(key)
  if entry
    entry[1]
  elsif block_given?
    yield(key)
  elsif default != Undefined
    default
  else
    raise KeyError, "key not found: #{key.inspect}"
  end
end

#find {|key, value| ... } ⇒ Array Also known as: detect

Yield ‘[key, value]` pairs until one is found for which the block returns true. Return that `[key, value]` pair. If the block never returns true, return `nil`.

Examples:

h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
h.find { |k, v| v.even? }
# => ["B", 2]

Yields:

  • (key, value)

    At most once for each key/value pair, until the block returns ‘true`.

Yield Returns:

  • Truthy to halt iteration and return the yielded key/value pair.

Returns:

  • (Array)


450
451
452
453
454
# File 'lib/immutable/hash.rb', line 450

def find
  return enum_for(:find) unless block_given?
  each { |entry| return entry if yield entry }
  nil
end

#flatten(level = 1) ⇒ Vector

Return a new Vector which is a one-dimensional flattening of this ‘Hash`. If `level` is 1, all the `[key, value]` pairs in the hash will be concatenated into one Vector. If `level` is greater than 1, keys or values which are themselves `Array`s or Vectors will be recursively flattened into the output Vector. The depth to which that flattening will be recursively applied is determined by `level`.

As a special case, if ‘level` is 0, each `[key, value]` pair will be a separate element in the returned Vector.

Examples:

h = Immutable::Hash["A" => 1, "B" => [2, 3, 4]]
h.flatten
# => Immutable::Vector["A", 1, "B", [2, 3, 4]]
h.flatten(2)
# => Immutable::Vector["A", 1, "B", 2, 3, 4]

Parameters:

  • level (Integer) (defaults to: 1)

    The number of times to recursively flatten the ‘[key, value]` pairs in this `Hash`.

Returns:



644
645
646
647
648
649
650
# File 'lib/immutable/hash.rb', line 644

def flatten(level = 1)
  return Vector.new(self) if level == 0
  array = []
  each { |k,v| array << k; array << v }
  array.flatten!(level-1) if level > 1
  Vector.new(array.freeze)
end

#get(key) ⇒ Object Also known as: []

Retrieve the value corresponding to the provided key object. If not found, and this ‘Hash` has a default block, the default block is called to provide the value. Otherwise, return `nil`.

Examples:

h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
h["B"]             # => 2
h.get("B")         # => 2
h.get("Elephant")  # => nil

# Immutable Hash with a default proc:
h = Immutable::Hash.new("A" => 1, "B" => 2, "C" => 3) { |key| key.size }
h.get("B")         # => 2
h.get("Elephant")  # => 8

Parameters:

  • key (Object)

    The key to look up

Returns:

  • (Object)


177
178
179
180
181
182
183
184
# File 'lib/immutable/hash.rb', line 177

def get(key)
  entry = @trie.get(key)
  if entry
    entry[1]
  elsif @default
    @default.call(key)
  end
end

#hashInteger

See ‘Object#hash`.

Returns:

  • (Integer)


739
740
741
742
743
# File 'lib/immutable/hash.rb', line 739

def hash
  keys.to_a.sort.reduce(0) do |hash, key|
    (hash << 32) - hash + key.hash + get(key).hash
  end
end

#inspectString

Return the contents of this ‘Hash` as a programmer-readable `String`. If all the keys and values are serializable as Ruby literal strings, the returned string can be passed to `eval` to reconstitute an equivalent `Hash`. The default block (if there is one) will be lost when doing this, however.

Returns:

  • (String)


751
752
753
754
755
756
757
758
759
760
# File 'lib/immutable/hash.rb', line 751

def inspect
  result = "#{self.class}["
  i = 0
  each do |key, val|
    result << ', ' if i > 0
    result << key.inspect << ' => ' << val.inspect
    i += 1
  end
  result << "]"
end

#invertHash

Return a new ‘Hash` created by using keys as values and values as keys. If there are multiple values which are equivalent (as determined by `#hash` and `#eql?`), only one out of each group of equivalent values will be retained. Which one specifically is undefined.

Examples:

Immutable::Hash["A" => 1, "B" => 2, "C" => 3, "D" => 2].invert
# => Immutable::Hash[1 => "A", 3 => "C", 2 => "B"]

Returns:



619
620
621
622
623
# File 'lib/immutable/hash.rb', line 619

def invert
  pairs = []
  each { |k,v| pairs << [v, k] }
  self.class.new(pairs, &@default)
end

#key(value) ⇒ Object

Searches through the ‘Hash`, comparing `value` with each value (using `#==`). When a matching value is found, return its associated key object. Return `nil` if no match is found.

Examples:

Immutable::Hash["A" => 1, "B" => 2, "C" => 3].key(2)  # => "B"

Parameters:

  • value (Object)

    The value to search for (using #==)

Returns:

  • (Object)


689
690
691
692
# File 'lib/immutable/hash.rb', line 689

def key(value)
  each { |entry| return entry[0] if value == entry[1] }
  nil
end

#key?(key) ⇒ Boolean Also known as: has_key?, include?, member?

Return ‘true` if the given key object is present in this `Hash`. More precisely, return `true` if a key with the same `#hash` code, and which is also `#eql?` to the given key object is present.

Examples:

Immutable::Hash["A" => 1, "B" => 2, "C" => 3].key?("B")  # => true

Parameters:

  • key (Object)

    The key to check for

Returns:

  • (Boolean)


140
141
142
# File 'lib/immutable/hash.rb', line 140

def key?(key)
  @trie.key?(key)
end

#keysSet

Return a new Set containing the keys from this ‘Hash`.

Examples:

Immutable::Hash["A" => 1, "B" => 2, "C" => 3, "D" => 2].keys
# => Immutable::Set["D", "C", "B", "A"]

Returns:



594
595
596
# File 'lib/immutable/hash.rb', line 594

def keys
  Set.alloc(@trie)
end

#map {|key, value| ... } ⇒ Hash Also known as: collect

Call the block once for each key/value pair in this ‘Hash`, passing the key/value pair as parameters. The block should return a `[key, value]` array each time. All the returned `[key, value]` arrays will be gathered into a new `Hash`.

Examples:

h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
h.map { |k, v| ["new-#{k}", v * v] }
# => Hash["new-C" => 9, "new-B" => 4, "new-A" => 1]

Yields:

  • (key, value)

    Once for each key/value pair.

Returns:



415
416
417
418
419
# File 'lib/immutable/hash.rb', line 415

def map
  return enum_for(:map) unless block_given?
  return self if empty?
  self.class.new(super, &@default)
end

#marshal_dump::Hash

Returns:

  • (::Hash)


806
807
808
# File 'lib/immutable/hash.rb', line 806

def marshal_dump
  to_hash
end

#marshal_load(dictionary) ⇒ Object



811
812
813
# File 'lib/immutable/hash.rb', line 811

def marshal_load(dictionary)
  @trie = Trie[dictionary]
end

#merge(other) {|key, my_value, other_value| ... } ⇒ Hash

Return a new ‘Hash` containing all the key/value pairs from this `Hash` and `other`. If no block is provided, the value for entries with colliding keys will be that from `other`. Otherwise, the value for each duplicate key is determined by calling the block.

‘other` can be an `Immutable::Hash`, a built-in Ruby `Hash`, or any `Enumerable` object which yields `[key, value]` pairs.

Examples:

h1 = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
h2 = Immutable::Hash["C" => 70, "D" => 80]
h1.merge(h2)
# => Immutable::Hash["C" => 70, "A" => 1, "D" => 80, "B" => 2]
h1.merge(h2) { |key, v1, v2| v1 + v2 }
# => Immutable::Hash["C" => 73, "A" => 1, "D" => 80, "B" => 2]

Parameters:

Yield Parameters:

  • key (Object)

    The key which was present in both collections

  • my_value (Object)

    The associated value from this ‘Hash`

  • other_value (Object)

    The associated value from the other collection

Yield Returns:

  • (Object)

    The value to associate this key with in the new ‘Hash`

Returns:



479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
# File 'lib/immutable/hash.rb', line 479

def merge(other)
  trie = if block_given?
    other.reduce(@trie) do |trie, (key, value)|
      if entry = trie.get(key)
        trie.put(key, yield(key, entry[1], value))
      else
        trie.put(key, value)
      end
    end
  else
    @trie.bulk_put(other)
  end

  derive_new_hash(trie)
end

#pretty_print(pp) ⇒ Object

Allows this ‘Hash` to be printed at the `pry` console, or using `pp` (from the Ruby standard library), in a way which takes the amount of horizontal space on the screen into account, and which indents nested structures to make them easier to read.



776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
# File 'lib/immutable/hash.rb', line 776

def pretty_print(pp)
  pp.group(1, "#{self.class}[", "]") do
    pp.breakable ''
    pp.seplist(self, nil) do |key, val|
      pp.group do
        key.pretty_print(pp)
        pp.text ' => '
        pp.group(1) do
          pp.breakable ''
          val.pretty_print(pp)
        end
      end
    end
  end
end

#put(key, value = yield(get(key))) {|value| ... } ⇒ Hash

Return a new ‘Hash` with the existing key/value associations, plus an association between the provided key and value. If an equivalent key is already present, its associated value will be replaced with the provided one.

If the ‘value` argument is missing, but an optional code block is provided, it will be passed the existing value (or `nil` if there is none) and what it returns will replace the existing value. This is useful for “transforming” the value associated with a certain key.

Avoid mutating objects which are used as keys. ‘String`s are an exception: unfrozen `String`s which are used as keys are internally duplicated and frozen. This matches RubyHash’s behaviour.

Examples:

h = Immutable::Hash["A" => 1, "B" => 2]
h.put("C", 3)
# => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
h.put("B") { |value| value * 10 }
# => Immutable::Hash["A" => 1, "B" => 20]

Parameters:

  • key (Object)

    The key to store

  • value (Object) (defaults to: yield(get(key)))

    The value to associate it with

Yields:

  • (value)

    The previously stored value, or ‘nil` if none.

Yield Returns:

  • (Object)

    The new value to store

Returns:



258
259
260
261
262
263
264
265
# File 'lib/immutable/hash.rb', line 258

def put(key, value = yield(get(key)))
  new_trie = @trie.put(key, value)
  if new_trie.equal?(@trie)
    self
  else
    self.class.alloc(new_trie, @default)
  end
end

#rassoc(obj) ⇒ Array

Searches through the ‘Hash`, comparing `obj` with each value (using `#==`). When a matching value is found, return the `[key, value]` pair as an array. Return `nil` if no match is found.

Examples:

Immutable::Hash["A" => 1, "B" => 2, "C" => 3].rassoc(2)  # => ["B", 2]

Parameters:

  • obj (Object)

    The value to search for (using #==)

Returns:

  • (Array)


675
676
677
678
# File 'lib/immutable/hash.rb', line 675

def rassoc(obj)
  each { |entry| return entry if obj == entry[1] }
  nil
end

#reverse_each {|key, value| ... } ⇒ self

Call the block once for each key/value pair in this ‘Hash`, passing the key/value pair as parameters. Iteration order will be the opposite of #each.

Examples:

Immutable::Hash["A" => 1, "B" => 2, "C" => 3].reverse_each { |k, v| puts "k=#{k} v=#{v}" }

k=B v=2
k=C v=3
k=A v=1
# => Immutable::Hash["A" => 1, "B" => 2, "C" => 3]

Yields:

  • (key, value)

    Once for each key/value pair.

Returns:

  • (self)


360
361
362
363
364
# File 'lib/immutable/hash.rb', line 360

def reverse_each(&block)
  return enum_for(:reverse_each) if not block_given?
  @trie.reverse_each(&block)
  self
end

#sampleArray

Return a randomly chosen ‘[key, value]` pair from this `Hash`. If the hash is empty, return `nil`.

Examples:

Immutable::Hash["A" => 1, "B" => 2, "C" => 3].sample
# => ["C", 3]

Returns:

  • (Array)


702
703
704
# File 'lib/immutable/hash.rb', line 702

def sample
  @trie.at(rand(size))
end

#select {|key, value| ... } ⇒ Hash Also known as: find_all, keep_if

Return a new ‘Hash` with all the key/value pairs for which the block returns true.

Examples:

h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
h.select { |k, v| v >= 2 }
# => Immutable::Hash["B" => 2, "C" => 3]

Yields:

  • (key, value)

    Once for each key/value pair.

Yield Returns:

  • Truthy if this pair should be present in the new ‘Hash`.

Returns:



432
433
434
435
# File 'lib/immutable/hash.rb', line 432

def select(&block)
  return enum_for(:select) unless block_given?
  derive_new_hash(@trie.select(&block))
end

#sizeInteger Also known as: length

Return the number of key/value pairs in this ‘Hash`.

Examples:

Immutable::Hash["A" => 1, "B" => 2, "C" => 3].size  # => 3

Returns:

  • (Integer)


119
120
121
# File 'lib/immutable/hash.rb', line 119

def size
  @trie.size
end

#slice(*wanted) ⇒ Hash

Return a new ‘Hash` with only the associations for the `wanted` keys retained.

Examples:

h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
h.slice("B", "C")  # => Immutable::Hash["B" => 2, "C" => 3]

Parameters:

Returns:



566
567
568
569
570
# File 'lib/immutable/hash.rb', line 566

def slice(*wanted)
  trie = Trie.new(0)
  wanted.each { |key| trie.put!(key, get(key)) if key?(key) }
  self.class.alloc(trie, @default)
end

#sortVector #sort({ |(k1, v1), (k2, v2)| ... }) {|(k1, v1), (k2, v2)| ... } ⇒ Vector

Return a sorted Vector which contains all the ‘[key, value]` pairs in this `Hash` as two-element `Array`s.

Overloads:

  • #sortVector

    Uses ‘#<=>` to determine sorted order.

  • #sort({ |(k1, v1), (k2, v2)| ... }) {|(k1, v1), (k2, v2)| ... } ⇒ Vector

    Uses the block as a comparator to determine sorted order.

    Examples:

    h = Immutable::Hash["Dog" => 1, "Elephant" => 2, "Lion" => 3]
    h.sort { |(k1, v1), (k2, v2)| k1.size  <=> k2.size }
    # => Immutable::Vector[["Dog", 1], ["Lion", 3], ["Elephant", 2]]

    Yields:

    • ((k1, v1), (k2, v2))

      Any number of times with different pairs of key/value associations.

    Yield Returns:

    • (Integer)

      Negative if the first pair should be sorted lower, positive if the latter pair, or 0 if equal.

Returns:

See Also:

  • Enumerable#sort


523
524
525
# File 'lib/immutable/hash.rb', line 523

def sort
  Vector.new(super)
end

#sort_by {|key, value| ... } ⇒ Vector

Return a Vector which contains all the ‘[key, value]` pairs in this `Hash` as two-element Arrays. The order which the pairs will appear in is determined by passing each pair to the code block to obtain a sort key object, and comparing the sort keys using `#<=>`.

Examples:

h = Immutable::Hash["Dog" => 1, "Elephant" => 2, "Lion" => 3]
h.sort_by { |key, value| key.size }
# => Immutable::Vector[["Dog", 1], ["Lion", 3], ["Elephant", 2]]

Yields:

  • (key, value)

    Once for each key/value pair.

Yield Returns:

  • a sort key object for the yielded pair.

Returns:

See Also:

  • Enumerable#sort_by


542
543
544
# File 'lib/immutable/hash.rb', line 542

def sort_by
  Vector.new(super)
end

#store(key, value) ⇒ Hash

An alias for #put to match RubyHash’s API. Does not support #put‘s block form.

Parameters:

  • key (Object)

    The key to store

  • value (Object)

    The value to associate it with

Returns:

See Also:



309
310
311
# File 'lib/immutable/hash.rb', line 309

def store(key, value)
  put(key, value)
end

#to_hash::Hash Also known as: to_h

Convert this ‘Immutable::Hash` to an instance of Ruby’s built-in ‘Hash`.

Returns:

  • (::Hash)


795
796
797
798
799
800
801
# File 'lib/immutable/hash.rb', line 795

def to_hash
  output = {}
  each do |key, value|
    output[key] = value
  end
  output
end

#update_in(*key_path) {|value| ... } ⇒ Hash

Return a new ‘Hash` with a deeply nested value modified to the result of the given code block. When traversing the nested `Hash`es and `Vector`s, non-existing keys are created with empty `Hash` values.

The code block receives the existing value of the deeply nested key (or ‘nil` if it doesn’t exist). This is useful for “transforming” the value associated with a certain key.

Note that the original ‘Hash` and sub-`Hash`es and sub-`Vector`s are left unmodified; new data structure copies are created along the path wherever needed.

Examples:

hash = Immutable::Hash["a" => Immutable::Hash["b" => Immutable::Hash["c" => 42]]]
hash.update_in("a", "b", "c") { |value| value + 5 }
# => Immutable::Hash["a" => Immutable::Hash["b" => Immutable::Hash["c" => 47]]]

Parameters:

  • key_path (::Array<Object>)

    List of keys which form the path to the key to be modified

Yields:

  • (value)

    The previously stored value

Yield Returns:

  • (Object)

    The new value to store

Returns:



288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/immutable/hash.rb', line 288

def update_in(*key_path, &block)
  if key_path.empty?
    raise ArgumentError, "must have at least one key in path"
  end
  key = key_path[0]
  if key_path.size == 1
    new_value = block.call(get(key))
  else
    value = fetch(key, EmptyHash)
    new_value = value.update_in(*key_path[1..-1], &block)
  end
  put(key, new_value)
end

#value?(value) ⇒ Boolean Also known as: has_value?

Return ‘true` if this `Hash` has one or more keys which map to the provided value.

Examples:

Immutable::Hash["A" => 1, "B" => 2, "C" => 3].value?(2)  # => true

Parameters:

  • value (Object)

    The value to check for

Returns:

  • (Boolean)


154
155
156
157
# File 'lib/immutable/hash.rb', line 154

def value?(value)
  each { |k,v| return true if value == v }
  false
end

#valuesVector

Return a new Vector populated with the values from this ‘Hash`.

Examples:

Immutable::Hash["A" => 1, "B" => 2, "C" => 3, "D" => 2].values
# => Immutable::Vector[2, 3, 2, 1]

Returns:



605
606
607
# File 'lib/immutable/hash.rb', line 605

def values
  Vector.new(each_value.to_a.freeze)
end

#values_at(*wanted) ⇒ Vector

Return a Vector of the values which correspond to the ‘wanted` keys. If any of the `wanted` keys are not present in this `Hash`, they will be skipped.

Examples:

h = Immutable::Hash["A" => 1, "B" => 2, "C" => 3]
h.values_at("B", "A", "D")  # => Immutable::Vector[2, 1]

Parameters:

  • wanted (Array)

    The keys to retrieve

Returns:



581
582
583
584
585
# File 'lib/immutable/hash.rb', line 581

def values_at(*wanted)
  array = []
  wanted.each { |key| array << get(key) if key?(key) }
  Vector.new(array.freeze)
end