Class: Hamster::Hash
- Inherits:
-
Object
- Object
- Hamster::Hash
- Includes:
- Associable, Enumerable, Immutable
- Defined in:
- lib/hamster/hash.rb
Overview
A Hamster::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 a Hamster::Hash.
A Hamster::Hash can be created in a couple of ways:
Hamster::Hash.new(font_size: 10, font_family: 'Arial')
Hamster::Hash[first_name: 'John', last_name: 'Smith']
Any Enumerable object which yields two-element [key, value] arrays
can be used to initialize a Hamster::Hash:
Hamster::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 = Hamster::Hash[a: 100, b: 200]
hash.put(:c, 500) # => Hamster::Hash[:a => 100, :b => 200, :c => 500]
hash # => Hamster::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 } # => Hamster::Hash[:a => 300, :b => 200]
Since it is immutable, all methods which you might expect to "modify" a
Hamster::Hash actually return a new hash and leave the existing one
unchanged. This means that the hash[key] = value syntax from RubyHash
cannot be used with Hamster::Hash.
Nested data structures can easily be updated using Associable#update_in:
hash = Hamster::Hash["a" => Hamster::Vector[Hamster::Hash["c" => 42]]]
hash.update_in("a", 0, "c") { |value| value + 5 }
# => Hamster::Hash["a" => Hamster::Hash["b" => Hamster::Hash["c" => 47]]]
While a Hamster::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, a Hamster::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 = Hamster::Hash.new { |missing_key| missing_key * 10 }
hash[5] # => 50
Class Method Summary collapse
-
.[](pairs = nil) ⇒ Hash
Create a new
Hashpopulated with the given key/value pairs. -
.alloc(trie = EmptyTrie, block = nil) ⇒ Hash
"Raw" allocation of a new
Hash. -
.empty ⇒ Hash
Return an empty
Hash.
Instance Method Summary collapse
-
#<(other) ⇒ Boolean
Return true if this
Hashis a proper subset ofother, which means all its keys are contained inotherwith the identical values, and the two hashes are not identical. -
#<=(other) ⇒ Boolean
Return true if this
Hashis a subset ofother, which means all its keys are contained inotherwith the identical values, and the two hashes are not identical. -
#==(other) ⇒ Boolean
Return true if
otherhas the same contents as thisHash. -
#>(other) ⇒ Boolean
Return true if this
Hashis a proper superset ofother, which means allother's keys are contained in thisHashwith the identical values, and the two hashes are not identical. -
#>=(other) ⇒ Boolean
Return true if this
Hashis a superset ofother, which means allother's keys are contained in thisHashwith the identical values. -
#assoc(obj) ⇒ Array
Searches through the
Hash, comparingobjwith each key (using#==). -
#clear ⇒ Hash
Return an empty
Hashinstance, of the same class as this one. -
#default_proc ⇒ Proc
Return the default block if there is one.
-
#delete(key) ⇒ Hash
Return a new
Hashwithkeyremoved. -
#each {|key, value| ... } ⇒ self
(also: #each_pair)
Call the block once for each key/value pair in this
Hash, passing the key/value pair as parameters. -
#each_key {|key| ... } ⇒ self
Call the block once for each key/value pair in this
Hash, passing the key as a parameter. -
#each_value {|value| ... } ⇒ self
Call the block once for each key/value pair in this
Hash, passing the value as a parameter. -
#empty? ⇒ Boolean
Return
trueif thisHashcontains no key/value pairs. -
#eql?(other) ⇒ Boolean
Return true if
otherhas the same type and contents as thisHash. -
#except(*keys) ⇒ Hash
Return a new
Hashwith the associations for all of the givenkeysremoved. -
#fetch(key, default = Undefined) ⇒ Object
Retrieve the value corresponding to the given key object, or use the provided default value or block, or otherwise raise a
KeyError. -
#fetch_values(*wanted) ⇒ Vector
Return a Vector of the values which correspond to the
wantedkeys. -
#find {|key, value| ... } ⇒ Array
(also: #detect)
Yield
[key, value]pairs until one is found for which the block returns true. -
#flatten(level = 1) ⇒ Vector
Return a new Vector which is a one-dimensional flattening of this
Hash. -
#get(key) ⇒ Object
(also: #[])
Retrieve the value corresponding to the provided key object.
-
#hash ⇒ Integer
See
Object#hash. -
#initialize(pairs = nil) {|key| ... } ⇒ Hash
constructor
A new instance of Hash.
-
#inspect ⇒ String
Return the contents of this
Hashas a programmer-readableString. -
#invert ⇒ Hash
Return a new
Hashcreated by using keys as values and values as keys. -
#key(value) ⇒ Object
Searches through the
Hash, comparingvaluewith each value (using#==). -
#key?(key) ⇒ Boolean
(also: #has_key?, #include?, #member?)
Return
trueif the given key object is present in thisHash. -
#keys ⇒ Set
Return a new Set containing the keys from this
Hash. -
#map {|key, value| ... } ⇒ Hash
(also: #collect)
Call the block once for each key/value pair in this
Hash, passing the key/value pair as parameters. - #marshal_dump ⇒ ::Hash
- #marshal_load(dictionary) ⇒ Object
-
#merge(other) {|key, my_value, other_value| ... } ⇒ Hash
Return a new
Hashcontaining all the key/value pairs from thisHashandother. -
#pretty_print(pp) ⇒ Object
Allows this
Hashto be printed at thepryconsole, or usingpp(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. -
#put(key, value = yield(get(key))) {|value| ... } ⇒ Hash
Return a new
Hashwith the existing key/value associations, plus an association between the provided key and value. -
#rassoc(obj) ⇒ Array
Searches through the
Hash, comparingobjwith each value (using#==). -
#reverse_each {|key, value| ... } ⇒ self
Call the block once for each key/value pair in this
Hash, passing the key/value pair as parameters. -
#sample ⇒ Array
Return a randomly chosen
[key, value]pair from thisHash. -
#select {|key, value| ... } ⇒ Hash
(also: #find_all, #keep_if)
Return a new
Hashwith all the key/value pairs for which the block returns true. -
#size ⇒ Integer
(also: #length)
Return the number of key/value pairs in this
Hash. -
#slice(*wanted) ⇒ Hash
Return a new
Hashwith only the associations for thewantedkeys retained. -
#sort ⇒ Vector
Return a sorted Vector which contains all the
[key, value]pairs in thisHashas two-elementArrays. -
#sort_by {|key, value| ... } ⇒ Vector
Return a Vector which contains all the
[key, value]pairs in thisHashas two-element Arrays. -
#store(key, value) ⇒ Hash
An alias for #put to match RubyHash's API.
-
#to_hash ⇒ ::Hash
(also: #to_h)
Convert this
Hamster::Hashto an instance of Ruby's built-inHash. -
#to_proc ⇒ Proc
Return a Proc which accepts a key as an argument and returns the value.
-
#value?(value) ⇒ Boolean
(also: #has_value?)
Return
trueif thisHashhas one or more keys which map to the provided value. -
#values ⇒ Vector
Return a new Vector populated with the values from this
Hash. -
#values_at(*wanted) ⇒ Vector
Return a Vector of the values which correspond to the
wantedkeys.
Methods included from Associable
Methods included from Enumerable
#<=>, #compact, #each_index, #grep, #grep_v, #group_by, #join, #partition, #product, #reject, #sum, #to_set
Methods included from Enumerable
Methods included from Immutable
Constructor Details
Class Method Details
.[](pairs = nil) ⇒ Hash
Create a new Hash populated with the given key/value pairs.
75 76 77 |
# File 'lib/hamster/hash.rb', line 75 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.
92 93 94 95 96 97 |
# File 'lib/hamster/hash.rb', line 92 def alloc(trie = EmptyTrie, block = nil) obj = allocate obj.instance_variable_set(:@trie, trie) obj.instance_variable_set(:@default, block) obj end |
.empty ⇒ Hash
Return an empty Hash. If used on a subclass, returns an empty instance
of that class.
83 84 85 |
# File 'lib/hamster/hash.rb', line 83 def empty @empty ||= self.new end |
Instance Method Details
#<(other) ⇒ Boolean
Return true if this Hash is a proper subset of other, which means all
its keys are contained in other with the identical values, and the two
hashes are not identical.
773 774 775 |
# File 'lib/hamster/hash.rb', line 773 def <(other) other > self end |
#<=(other) ⇒ Boolean
Return true if this Hash is a subset of other, which means all its
keys are contained in other with the identical values, and the two
hashes are not identical.
783 784 785 |
# File 'lib/hamster/hash.rb', line 783 def <=(other) other >= self end |
#==(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.
739 740 741 |
# File 'lib/hamster/hash.rb', line 739 def ==(other) self.eql?(other) || (other.respond_to?(:to_hash) && to_hash.eql?(other.to_hash)) end |
#>(other) ⇒ Boolean
Return true if this Hash is a proper superset of other, which means
all other's keys are contained in this Hash with the identical
values, and the two hashes are not identical.
749 750 751 |
# File 'lib/hamster/hash.rb', line 749 def >(other) self != other && self >= other end |
#>=(other) ⇒ Boolean
Return true if this Hash is a superset of other, which means all
other's keys are contained in this Hash with the identical values.
758 759 760 761 762 763 764 765 |
# File 'lib/hamster/hash.rb', line 758 def >=(other) other.each do |key, value| if self[key] != value return false end end true 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.
667 668 669 670 |
# File 'lib/hamster/hash.rb', line 667 def assoc(obj) each { |entry| return entry if obj == entry[0] } nil end |
#clear ⇒ Hash
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.
717 718 719 720 721 722 723 |
# File 'lib/hamster/hash.rb', line 717 def clear if @default self.class.alloc(EmptyTrie, @default) else self.class.empty end end |
#default_proc ⇒ Proc
Return the default block if there is one. Otherwise, return nil.
111 112 113 |
# File 'lib/hamster/hash.rb', line 111 def default_proc @default end |
#delete(key) ⇒ Hash
Return a new Hash with key removed. If key is not present, return
self.
311 312 313 |
# File 'lib/hamster/hash.rb', line 311 def delete(key) derive_new_hash(@trie.delete(key)) 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.
329 330 331 332 333 |
# File 'lib/hamster/hash.rb', line 329 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.
368 369 370 371 372 |
# File 'lib/hamster/hash.rb', line 368 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.
387 388 389 390 391 |
# File 'lib/hamster/hash.rb', line 387 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.
129 130 131 |
# File 'lib/hamster/hash.rb', line 129 def empty? @trie.empty? end |
#eql?(other) ⇒ Boolean
Return true if other has the same type and contents as this Hash.
729 730 731 732 |
# File 'lib/hamster/hash.rb', line 729 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.
543 544 545 |
# File 'lib/hamster/hash.rb', line 543 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.
222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/hamster/hash.rb', line 222 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 |
#fetch_values(*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, raise KeyError
exception.
588 589 590 591 |
# File 'lib/hamster/hash.rb', line 588 def fetch_values(*wanted) array = wanted.map { |key| fetch(key) } Vector.new(array.freeze) 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.
439 440 441 442 443 |
# File 'lib/hamster/hash.rb', line 439 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 Arrays 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.
650 651 652 653 654 655 656 |
# File 'lib/hamster/hash.rb', line 650 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.
179 180 181 182 183 184 185 186 |
# File 'lib/hamster/hash.rb', line 179 def get(key) entry = @trie.get(key) if entry entry[1] elsif @default @default.call(key) end end |
#hash ⇒ Integer
See Object#hash.
789 790 791 792 793 |
# File 'lib/hamster/hash.rb', line 789 def hash keys.to_a.sort.reduce(0) do |hash, key| (hash << 32) - hash + key.hash + get(key).hash end end |
#inspect ⇒ String
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.
801 802 803 804 805 806 807 808 809 810 |
# File 'lib/hamster/hash.rb', line 801 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 |
#invert ⇒ Hash
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.
625 626 627 628 629 |
# File 'lib/hamster/hash.rb', line 625 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.
695 696 697 698 |
# File 'lib/hamster/hash.rb', line 695 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.
142 143 144 |
# File 'lib/hamster/hash.rb', line 142 def key?(key) @trie.key?(key) end |
#keys ⇒ Set
Return a new Set containing the keys from this Hash.
600 601 602 |
# File 'lib/hamster/hash.rb', line 600 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.
404 405 406 407 408 |
# File 'lib/hamster/hash.rb', line 404 def map return enum_for(:map) unless block_given? return self if empty? self.class.new(super, &@default) end |
#marshal_dump ⇒ ::Hash
864 865 866 |
# File 'lib/hamster/hash.rb', line 864 def marshal_dump to_hash end |
#marshal_load(dictionary) ⇒ Object
869 870 871 |
# File 'lib/hamster/hash.rb', line 869 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 a Hamster::Hash, a built-in Ruby Hash, or any Enumerable
object which yields [key, value] pairs.
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 |
# File 'lib/hamster/hash.rb', line 468 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.
818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 |
# File 'lib/hamster/hash.rb', line 818 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. Strings are an exception:
unfrozen Strings which are used as keys are internally duplicated and
frozen. This matches RubyHash's behaviour.
260 261 262 263 264 265 266 267 |
# File 'lib/hamster/hash.rb', line 260 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.
681 682 683 684 |
# File 'lib/hamster/hash.rb', line 681 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.
349 350 351 352 353 |
# File 'lib/hamster/hash.rb', line 349 def reverse_each(&block) return enum_for(:reverse_each) if not block_given? @trie.reverse_each(&block) self end |
#sample ⇒ Array
Return a randomly chosen [key, value] pair from this Hash. If the hash is empty,
return nil.
708 709 710 |
# File 'lib/hamster/hash.rb', line 708 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.
421 422 423 424 |
# File 'lib/hamster/hash.rb', line 421 def select(&block) return enum_for(:select) unless block_given? derive_new_hash(@trie.select(&block)) end |
#size ⇒ Integer Also known as: length
Return the number of key/value pairs in this Hash.
121 122 123 |
# File 'lib/hamster/hash.rb', line 121 def size @trie.size end |
#slice(*wanted) ⇒ Hash
Return a new Hash with only the associations for the wanted keys retained.
555 556 557 558 559 |
# File 'lib/hamster/hash.rb', line 555 def slice(*wanted) trie = Trie.new(0) wanted.each { |key| trie.put!(key, get(key)) if key?(key) } self.class.alloc(trie, @default) end |
#sort ⇒ Vector #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 Arrays.
512 513 514 |
# File 'lib/hamster/hash.rb', line 512 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 #<=>.
531 532 533 |
# File 'lib/hamster/hash.rb', line 531 def sort_by Vector.new(super) end |
#store(key, value) ⇒ Hash
298 299 300 |
# File 'lib/hamster/hash.rb', line 298 def store(key, value) put(key, value) end |
#to_hash ⇒ ::Hash Also known as: to_h
Convert this Hamster::Hash to an instance of Ruby's built-in Hash.
837 838 839 840 841 842 843 |
# File 'lib/hamster/hash.rb', line 837 def to_hash output = {} each do |key, value| output[key] = value end output end |
#to_proc ⇒ Proc
Return a Proc which accepts a key as an argument and returns the value. The Proc behaves like #get (when the key is missing, it returns nil or result of the default proc).
858 859 860 |
# File 'lib/hamster/hash.rb', line 858 def to_proc lambda { |key| get(key) } 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.
156 157 158 159 |
# File 'lib/hamster/hash.rb', line 156 def value?(value) each { |k,v| return true if value == v } false end |
#values ⇒ Vector
Return a new Vector populated with the values from this Hash.
611 612 613 |
# File 'lib/hamster/hash.rb', line 611 def values Vector.new(each_value.to_a.freeze) end |