module Gorillib::Hashlike
Your class must provide [], []=, delete, and keys –
-
hsh Element Reference – Retrieves the value stored for
key
. -
hsh = val Element Assignment – Associates
val
withkey
. -
hsh.delete(key) Deletes & returns the value whose key is equal to
key
. -
hsh.keys Returns a new array populated with the keys.
(see Hashlike::HashlikeViaAccessors
for example)
Given the above, hashlike will provide the rest, defining the methods
:each_pair, :each, :each_key, :each_value, :values_at, :values_of, :values, :size, :length, :has_key?, :include?, :key?, :member?, :has_value?, :value?, :fetch, :key, :assoc, :rassoc, :empty?, :merge, :update, :merge!, :reject!, :select!, :delete_if, :keep_if, :reject, :clear, :store, :to_hash, :invert, :flatten
and these methods added by Enumerable:
:each_cons, :each_entry, :each_slice, :each_with_index, :each_with_object, :entries, :to_a, :map, :collect, :collect_concat, :group_by, :flat_map, :inject, :reduce, :chunk, :reverse_each, :slice_before, :drop, :drop_while, :take, :take_while, :detect, :find, :find_all, :select, :find_index, :grep, :all?, :any?, :none?, :one?, :first, :count, :zip, :max, :max_by, :min, :min_by, :minmax, :minmax_by, :sort, :sort_by, :cycle, :partition,
It does not define these methods that do exist on hash:
:default, :default=, :default_proc, :default_proc=, :compare_by_identity, :compare_by_identity?, :replace, :rehash, :shift
Chinese wall¶ ↑
With a few exceptions, all methods are defined only in terms of
#[], #[]=, #delete, #keys, #each_pair and #has_key?
(exceptions: merge family depend on update
; the reject/select/xx_if family depend on each other; invert
& flatten
call to_hash
; rassoc
calls key
)
custom iterators¶ ↑
Hashlike
typically defines the following fundamental iterators by including Gorillib::Hashlike::EnumerateFromKeys
:
:each_pair, :each, :values, :values_at, :length
However, if the each_pair method already exists on the class (as it does for Struct), those methods will not be defined. The class is held responsible for the implementation of all five. (Of these, each_pair is the only method called from elsewhere in Hashlike
, while each is the only method called from Enumerable
).
convert_key (Indifferent Access)¶ ↑
If you define convert_key the values_at, has_key?
, fetch
, and assoc
methods will use it to sanitize keys coming in from the outside. It’s assumed that you will do the same with [], []= and delete. (see Gorillib::HashWithIndifferentAccess for an example).
Public Class Methods
# File lib/gorillib/hashlike.rb, line 806 def self.included(base) base.class_eval do include EnumerateFromKeys unless method_defined?(:each_pair) unless include?(Enumerable) include Enumerable include OverrideEnumerable end # included here so they win out over Enumerable alias_method :include?, :has_key? alias_method :key?, :has_key? alias_method :member?, :has_key? alias_method :value?, :has_value? alias_method :merge!, :update alias_method :size, :length end end
Public Instance Methods
Searches through the hashlike comparing obj with the key using ==. Returns the key-value pair (two elements array) or nil if no match is found.
@see Array#assoc.
@example
hsh = { "colors" => ["red", "blue", "green"], "letters" => [:a, :b, :c ]} hsh.assoc("letters") # => ["letters", [:a, :b, :c]] hsh.assoc("foo") # => nil
@return [Array, nil] the key-value pair (two elements array) or nil if no
match is found.
# File lib/gorillib/hashlike.rb, line 427 def assoc(key) key = convert_key(key) if respond_to?(:convert_key) return unless has_key?(key) [key, self[key]] end
Removes all key-value pairs from hsh
.
@example
hsh = { :a => 100, :b => 200 } # => { :a => 100, :b => 200 } hsh.clear # => {}
@return [Hashlike] this hashlike, emptied
# File lib/gorillib/hashlike.rb, line 730 def clear each_pair{|k,v| delete(k) } end
Deletes every key-value pair from hsh
for which block
evaluates truthy.
If no block is given, an enumerator is returned instead.
@example
hsh = { :a => 100, :b => 200, :c => 300 } hsh.delete_if{|key, val| key.to_s >= "b" } # => { :a => 100 } hsh = { :a => 100, :b => 200, :c => 300 } hsh.delete_if{|key, val| key.to_s >= "z" } # => { :a => 100, :b => 200, :c => 300 }
@overload hsh.delete_if{|key, val| block } -> hsh
Deletes every key-value pair from +hsh+ for which +block+ evaluates truthy. @return [Hashlike]
@overload hsh.delete_if -> an_enumerator
with no block, returns a raw enumerator @return [Enumerator]
# File lib/gorillib/hashlike.rb, line 625 def delete_if(&block) return enum_for(:delete_if) unless block_given? reject!(&block) self end
Calls block
once for each key in hsh
, passing the key as a parameter.
If no block is given, an enumerator is returned instead.
@example
hsh = { :a => 100, :b => 200 } hsh.each_key{|key| puts key } # produces: a b
@example with block arity:
hsh = {[:a,:b] => 3, [:c, :d] => 4, :e => 5} seen_args = [] hsh.each_key{|arg1, arg2, arg3| seen_args << [arg1, arg2, arg3] } # => [[:a, :b, nil], [:c, :d, nil], [:e, nil, nil]] seen_args = [] hsh.each_key{|(arg1, arg2), arg3| seen_args << [arg1, arg2, arg3] } # => [[:a, nil, :b], [:c, nil, :d], [:e, nil, nil]]
@overload hsh.each_key{|key| block } -> hsh
Calls +block+ once for each key in +hsh+ @yield [key] in order, each key @return [Hashlike]
@overload hsh.each_key -> an_enumerator
with no block, returns a raw enumerator @return [Enumerator]
# File lib/gorillib/hashlike.rb, line 250 def each_key return enum_for(:each_key) unless block_given? each_pair{|k,v| yield k } self end
Calls block
once for each key in hsh
, passing the value as a parameter.
If no block is given, an enumerator is returned instead.
@example
hsh = { :a => 100, :b => 200 } hsh.each_value{|value| puts value } # produces: 100 200
@example with block arity:
hsh = {:a => [300,333], :b => [400,444], :e => 500}) seen_args = [] hsh.each_value{|arg1, arg2, arg3| seen_args << [arg1, arg2, arg3] } # => [[300, 333, nil], [400, 444, nil], [500, nil, nil]] seen_args = [] hsh.each_value{|(arg1, arg2), arg3| seen_args << [arg1, arg2, arg3] } # => [[300, nil, 333], [400, nil, 444], [500, nil, nil]]
@overload hsh.each_value{|val| block } -> hsh
Calls +block+ once for each value in +hsh+ @yield [val] in order by its key, each value @return [Hashlike]
@overload hsh.each_value -> an_enumerator
with no block, returns a raw enumerator @return [Enumerator]
# File lib/gorillib/hashlike.rb, line 287 def each_value return enum_for(:each_value) unless block_given? each_pair{|k,v| yield v } self end
Returns true if the hashlike contains no key-value pairs, false otherwise.
@example
{}.empty? # => true
@return [true, false] true if hsh
contains no key-value pairs, false otherwise
# File lib/gorillib/hashlike.rb, line 461 def empty? keys.empty? end
Returns a value from the hashlike for the given key. If the key can’t be found, there are several options:
-
With no other arguments, it will raise a
KeyError
exception; -
if default is given, then that will be returned;
-
if the optional code block is specified, then that will be run and its result returned.
@example
hsh = { :a => 100, :b => 200 } hsh.fetch(:a) # => 100 hsh.fetch(:z, "go fish") # => "go fish" hsh.fetch(:z){|el| "go fish, #{el}"} # => "go fish, z"
@example An exception is raised if the key is not found and a default value is not supplied.
hsh = { :a => 100, :b => 200 } hsh.fetch(:z) # produces: prog.rb:2:in `fetch': key not found (KeyError) from prog.rb:2 hsh.fetch(:z, 3) # => 3 hsh.fetch(:z){|key| key.to_s * 5 } # => "zzzzz"
@param key [Object] the key to query @param default [Object] the value to use if the key is missing @raise [KeyError] raised if missing, and neither default
nor block
is supplied @yield [key] if missing, block called with the key requested @return [Object] the value; if missing, the default; if missing, the
block's return value
# File lib/gorillib/hashlike.rb, line 382 def fetch(key, default=nil, &block) key = convert_key(key) if respond_to?(:convert_key) warn "#{caller[0]}: warning: block supersedes default value argument" if default && block_given? if has_key?(key) then self[key] elsif block_given? then yield(key) elsif default then default else raise KeyError, "key not found: #{key.inspect}" end end
Returns a new array that is a one-dimensional flattening of this hashlike. That is, for every key or value that is an array, extract its elements into the new array. Unlike Array#flatten, this method does not flatten recursively by default; pass nil
explicitly to flatten recursively. The optional level argument determines the level of recursion to flatten.
@example
hsh = {1=> "one", 2 => [2,"two"], 3 => "three"} hsh.flatten # => [1, "one", 2, [2, "two"], 3, "three"] hsh.flatten(2) # => [1, "one", 2, 2, "two", 3, "three"]
@example with deep nesting
hsh = { [1, 2, [3, 4]] => [1, [2, 3, [4, 5, 6]]] } hsh.flatten # => [[1, 2, [3, 4]], [1, [2, 3, [4, 5, 6]]]] hsh.flatten(0) # => [[[1, 2, [3, 4]], [1, [2, 3, [4, 5, 6]]]]] hsh.flatten(1) # => [[1, 2, [3, 4]], [1, [2, 3, [4, 5, 6]]]] hsh.flatten(2) # => [1, 2, [3, 4], 1, [2, 3, [4, 5, 6]]] hsh.flatten(3) # => [1, 2, 3, 4, 1, 2, 3, [4, 5, 6]] hsh.flatten(4) # => [1, 2, 3, 4, 1, 2, 3, 4, 5, 6] hsh.flatten.flatten # => [1, 2, 3, 4, 1, 2, 3, 4, 5, 6]
@example nil level means complete flattening
hsh.flatten(nil) # => [1, 2, 3, 4, 1, 2, 3, 4, 5, 6]
@overload flatten @overload flatten(level)
@param level [Integer] the level of recursion to flatten, 0 by default.
@return [Array] the flattened key-value array.
# File lib/gorillib/hashlike.rb, line 802 def flatten(*args) to_hash.flatten(*args) end
Returns true if the given key is present in hsh
.
@example
hsh = { :a => 100, :b => 200 } hsh.has_key?(:a) # => true hsh.has_key?(:z) # => false
@param key [Object] the key to check for. @return [true, false] true if the key is present, false otherwise
# File lib/gorillib/hashlike.rb, line 328 def has_key?(key) key = convert_key(key) if respond_to?(:convert_key) keys.include?(key) end
Returns true if the given value is present for some key in hsh
.
@example
hsh = { :a => 100, :b => 200 } hsh.has_value?(100) # => true hsh.has_value?(999) # => false
@param target [Object] the value to query @return [true, false] true if the value is present, false otherwise
# File lib/gorillib/hashlike.rb, line 344 def has_value?(target) # don't refactor this to any? -- Struct's #any is weird each_pair{|key, val| return true if (val == target) } false end
Returns a new hash created by using hsh
‘s values as keys, and the keys as values. If hsh
has duplicate values, the result will contain only one of them as a key – which one is not predictable.
@example
hsh = { :n => 100, :m => 100, :y => 300, :d => 200, :a => 0 } hsh.invert # => { 0 => :a, 100 => :m, 200 => :d, 300 => :y }
@return [Hash] a new hash, with values for keys and vice-versa
# File lib/gorillib/hashlike.rb, line 759 def invert to_hash.invert end
Deletes every key-value pair from hsh
for which block
evaluates falsy.
If no block is given, an enumerator is returned instead.
@example
hsh = { :a => 100, :b => 200, :c => 300 } hsh.keep_if{|key, val| key.to_s >= "b" } # => { :b => 200, :c => 300 } hsh = { :a => 100, :b => 200, :c => 300 } hsh.keep_if{|key, val| key.to_s >= "a" } # => { :a => 100, :b => 200, :c => 300 }
@overload hsh.keep_if{|key, val| block } -> hsh
Deletes every key-value pair from +hsh+ for which +block+ evaluates falsy. @return [Hashlike]
@overload hsh.keep_if -> an_enumerator
with no block, returns a raw enumerator @return [Enumerator]
# File lib/gorillib/hashlike.rb, line 651 def keep_if(&block) return enum_for(:keep_if) unless block_given? select!(&block) self end
Searches the hash for an entry whose value == val
, returning the corresponding key. If not found, returns nil
.
You are guaranteed that the first matching key in keys will be the one returned.
@example
hsh = { :a => 100, :b => 200 } hsh.key(200) # => :b hsh.key(999) # => nil
@param val [Object] the value to look up @return [Object, nil] the key for the given val, or nil if missing
# File lib/gorillib/hashlike.rb, line 407 def key(val) keys.find{|key| self[key] == val } end
Returns a new hashlike containing the contents of other_hash
and the contents of hsh
. If no block is specified, the value for entries with duplicate keys will be that of other_hash
. Otherwise the value for each duplicate key is determined by calling the block with the key, its value in hsh
and its value in other_hash
.
@example
h1 = { :a => 100, :b => 200 } h2 = { :b => 254, :c => 300 } h1.merge(h2) # => { :a=>100, :b=>254, :c=>300 } h1.merge(h2){|key, oldval, newval| newval - oldval} # => { :a => 100, :b => 54, :c => 300 } h1 # => { :a => 100, :b => 200 }
@overload hsh.merge(other_hash) -> hsh
Adds the contents of +other_hash+ to +hsh+. Entries with duplicate keys are overwritten with the values from +other_hash+ @param other_hash [Hash, Hashlike] the hash to merge (it wins) @return [Hashlike] a new merged hashlike
@overload hsh.merge(other_hash){|key, oldval, newval| block} -> hsh
Adds the contents of +other_hash+ to +hsh+. The value of each duplicate key is determined by calling the block with the key, its value in +hsh+ and its value in +other_hash+. @param other_hash [Hash, Hashlike] the hash to merge (it wins) @yield [Object, Object, Object] called if key exists in each +hsh+ @return [Hashlike] a new merged hashlike
# File lib/gorillib/hashlike.rb, line 539 def merge(*args, &block) self.dup.update(*args, &block) end
Searches through the hashlike comparing obj with the value using ==. Returns the first key-value pair (two-element array) that matches, or nil if no match is found.
@see Array#rassoc.
@example
hsh = { 1 => "one", 2 => "two", 3 => "three", "ii" => "two"} hsh.rassoc("two") # => [2, "two"] hsh.rassoc("four") # => nil
@return [Array, nil] The first key-value pair (two-element array) that
matches, or nil if no match is found
# File lib/gorillib/hashlike.rb, line 448 def rassoc(val) key = key(val) or return [key, self[key]] end
Deletes every key-value pair from hsh
for which block
evaluates truthy (equivalent to Hashlike#delete_if
), but returns nil if no changes were made.
@example
hsh = { :a => 100, :b => 200, :c => 300 } hsh.delete_if{|key, val| key.to_s >= "b" } # => { :a => 100 } hsh = { :a => 100, :b => 200, :c => 300 } hsh.delete_if{|key, val| key.to_s >= "z" } # nil
@overload hsh.reject!{|key, val| block } -> hsh or nil
Deletes every key-value pair from +hsh+ for which +block+ evaluates truthy. @return [Hashlike, nil]
@overload hsh.reject! -> an_enumerator
with no block, returns a raw enumerator @return [Enumerator]
# File lib/gorillib/hashlike.rb, line 562 def reject!(&block) return enum_for(:reject!) unless block_given? changed = false each_pair do |key, val| if yield(*[key, val].take(block.arity)) changed = true delete(key) end end changed ? self : nil end
Deletes every key-value pair from hsh
for which block
evaluates falsy (equivalent to Hashlike#keep_if
), but returns nil if no changes were made.
@example
hsh = { :a => 100, :b => 200, :c => 300 } hsh.select!{|key, val| key.to_s >= "b" } # => { :b => 200, :c => 300 } hsh = { :a => 100, :b => 200, :c => 300 } hsh.select!{|key, val| key.to_s >= "a" } # => { :a => 100, :b => 200, :c => 300 }
@overload hsh.select!{|key, val| block } -> hsh or nil
Deletes every key-value pair from +hsh+ for which +block+ evaluates falsy. @return [Hashlike]
@overload hsh.select! -> an_enumerator
with no block, returns a raw enumerator @return [Enumerator]
# File lib/gorillib/hashlike.rb, line 593 def select!(&block) return enum_for(:select!) unless block_given? changed = false each_pair do |key, val| if not yield(*[key, val].take(block.arity)) changed = true delete(key) end end changed ? self : nil end
alias for []=
# File lib/gorillib/hashlike.rb, line 214 def store(key, val) self[key] = val end
Returns a hash with each key set to its associated value.
@example
my_hshlike = MyHashlike.new my_hshlike[:a] = 100; my_hshlike[:b] = 200 my_hshlike.to_hash # => { :a => 100, :b => 200 }
@return [Hash] a new Hash
instance, with each key set to its associated value.
# File lib/gorillib/hashlike.rb, line 744 def to_hash {}.tap{|hsh| each_pair{|key, val| hsh[key] = val } } end
Adds the contents of other_hash
to hsh
. If no block is specified, entries with duplicate keys are overwritten with the values from other_hash
, otherwise the value of each duplicate key is determined by calling the block with the key, its value in hsh
and its value in other_hash
.
@example
h1 = { :a => 100, :b => 200 } h2 = { :b => 254, :c => 300 } h1.merge!(h2) # => { :a => 100, :b => 254, :c => 300 } h1 = { :a => 100, :b => 200 } h2 = { :b => 254, :c => 300 } h1.merge!(h2){|key, v1, v2| v1 } # => { :a => 100, :b => 200, :c => 300 }
@overload hsh.update(other_hash) -> hsh
Adds the contents of +other_hash+ to +hsh+. Entries with duplicate keys are overwritten with the values from +other_hash+ @param other_hash [Hash, Hashlike] the hash to merge (it wins) @return [Hashlike] this hashlike, updated
@overload hsh.update(other_hash){|key, oldval, newval| block} -> hsh
Adds the contents of +other_hash+ to +hsh+. The value of each duplicate key is determined by calling the block with the key, its value in +hsh+ and its value in +other_hash+. @param other_hash [Hash, Hashlike] the hash to merge (it wins) @yield [Object, Object, Object] called if key exists in each +hsh+ @return [Hashlike] this hashlike, updated
# File lib/gorillib/hashlike.rb, line 497 def update(other_hash) raise TypeError, "can't convert #{other_hash.nil? ? 'nil' : other_hash.class} into Hash" unless other_hash.respond_to?(:each_pair) other_hash.each_pair do |key, val| if block_given? && has_key?(key) val = yield(key, val, self[key]) end self[key] = val end self end
Array
containing the values associated with the given keys.
@see Hashlike#select
.
@example
hsh = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" } hsh.values_of("cow", "cat") # => ["bovine", "feline"]
@example
hsh = { :a => 100, :b => 200, :c => 300 } hsh.values_of(:c, :a, :c, :z, :a) # => [300, 100, 300, nil, 100]
@param allowed_keys [Object] the keys to retrieve. @return [Array] the values, in order according to allowed_keys.
# File lib/gorillib/hashlike.rb, line 310 def values_of(*allowed_keys) allowed_keys.map do |key| key = convert_key(key) if respond_to?(:convert_key) self[key] if has_key?(key) end end