class Hash
Public Class Methods
Hash which auto initializes it's children.
h = Hash.autonew h['s1']['p1'] = 4 h['s1']['p2'] = 5 h['s1']['p3'] = 2 h['s1']['p4'] = 3 h #=> {"s1"=>{"p1"=>4, "p4"=>3, "p3"=>2, "p2"=>5}} h['s1'].keys.sort #=> ["p1", "p2", "p3", "p4"]
CREDIT: Trans, Jan Molic
# File lib/core/facets/hash/autonew.rb, line 18 def self.autonew(*args) leet = lambda{ |hsh, key| hsh[key] = new( &leet ) } new(*args,&leet) end
Instantiate a new hash with a default value determined by the block.
::new_with{ [] }
CREDIT: Pit Capitan
# File lib/core/facets/hash/new_with.rb, line 10 def self.new_with #:yield: new { |h, k| h[k] = yield } end
Creates a new hash from two separate arrays, a keys
array and
a values
array.
Hash.zip(["a","b","c"], [1,2,3]) # => { "a"=>1, "b"=>2, "c"=>3 }
CREDIT: Trans, Ara T. Howard
# File lib/core/facets/hash/zip.rb, line 11 def self.zip(keys,values) # or some better name h = {} keys.size.times{ |i| h[ keys[i] ] = values[i] } h end
Public Instance Methods
Hash intersection. Two hashes intersect when their pairs are equal.
({:a=>1,:b=>2} & {:a=>1,:c=>3}) #=> {:a=>1}
A hash can also be intersected with an array to intersect keys only.
({:a=>1,:b=>2} & [:a,:c]) #=> {:a=>1}
The later form is similar to pairs_at. The differ only in that pairs_at will return a nil value for a key not in the hash, but #& will not.
CREDIT: Trans
# File lib/core/facets/hash/op_and.rb, line 19 def &(other) case other when Array k = (keys & other) Hash[*(k.zip(values_at(*k)).flatten)] else x = (to_a & other.to_a).inject([]) do |a, kv| a.concat kv; a end Hash[*x] end end
Like merge operator '+' but merges in reverse order.
h1 = {:a=>1} h2 = {:a=>2, :b=>3} (h1 + h2) #=> { :a=>2, :b=>3 } (h1 * h2) #=> { :a=>1, :b=>3 }
CREDIT: Trans
# File lib/core/facets/hash/op_mul.rb, line 13 def *(other) other.merge(self) end
Operator for merge.
CREDIT: Trans
# File lib/core/facets/hash/op_add.rb, line 7 def +(other) merge(other) end
Operator for removing hash pairs. If another hash is given the pairs are only removed if both key and value are equal. If an array is given then matching keys are removed.
CREDIT: Trans CREDIT: Xavier Shay (bug fix)
# File lib/core/facets/hash/op_sub.rb, line 10 def -(other) h = self.dup if other.respond_to?(:to_ary) other.to_ary.each do |k| h.delete(k) end else other.each do |k,v| if h.key?(k) h.delete(k) if v == h[k] end end end h end
Can be used like update, or passed as two-element [key,value] array.
CREDIT: Trans
# File lib/core/facets/hash/op_push.rb, line 8 def <<(other) if other.respond_to?(:to_ary) store(*other) else update(other) end self end
Modifies the receiving Hash so that the value previously referred to by oldkey is also referenced by newkey; oldkey is retained in the Hash. If oldkey does not exist as a key in the Hash, no change is effected.
Returns a reference to the Hash.
foo = { :name=>'Gavin', 'wife'=>:Lisa } foo.alias!('name',:name) #=> { :name=>'Gavin', 'name'=>'Gavin', 'wife'=>:Lisa } foo = { :name=>'Gavin', 'wife'=>:Lisa } foo.alias!('spouse','wife') #=> { :name=>'Gavin', 'wife'=>:Lisa, 'spouse'=>:Lisa } foo = { :name=>'Gavin', 'wife'=>:Lisa } foo.alias!('bar','foo') #=> { :name=>'Gavin', 'wife'=>:Lisa }
Note that if the oldkey is reassigned, the reference will no longer exist, and the newkey will remain as it was.
CREDIT: Gavin Sinclair
TODO: Rename to aliaskey or something else.
# File lib/core/facets/hash/alias.rb, line 25 def alias!(newkey, oldkey) self[newkey] = self[oldkey] if self.has_key?(oldkey) self end
Turn a hash into a method arguments.
h = { :list => [1,2], :base => "HI" }
Without an argument field.
h.argumentize #=> [ { :list => [1,2], :base => "HI" } ]
With an argument field.
h.argumentize(:list) #=> [ 1, 2, { :base => "HI" } ] h.argumentize(:base) #=> [ "HI", { :list => [1,2] } ]
# File lib/core/facets/hash/argumentize.rb, line 16 def argumentize(args_field=nil) config = dup if args_field args = [config.delete(args_field)].flatten.compact else args = [] end args << config return args end
Merge the values of this hash with those from another, setting all values to be arrays representing the values from both hashes.
{ :a=>1, :b=>2 }.collate(:a=>3, :b=>4, :c=>5) #=> { :a=>[1,3], :b=>[2,4], :c=>[5] }
As of v3.0, this method no longer automatically flattens the array values. To acheive the same effect add a flat map.
h = { :a=>[1,3], :b=>[2,4], :c=>[5] }.collate(:a=>6, :b=>7, :c=>8) h.each_value{ |v| v.flatten! } #=> { :a=>[1,3,6], :b=>[2,4,7], :c=>[5,8] }
@author Trans (rewrite) @author Tilo Sloboda (bug fixes) @author Gavin Kistner (original)
# File lib/core/facets/hash/collate.rb, line 20 def collate(*others) h = {} keys.each do |k| h[k] = [] end others.each do |other| other.keys.each do |k| h[k] = [] end end each do |k, v| h[k] << v end others.each do |other| other.each do |k, v| h[k] << v end end h.each{ |k,v| v.flatten! } h end
The same as collate, but modifies the receiver in place.
# File lib/core/facets/hash/collate.rb, line 43 def collate!(other_hash) result = collate(other_hash) replace(result) end
Like Enumerable#count, but can count hash values.
{:A=>1, :B=>1}.count(1) #=> 2
# File lib/core/facets/hash/count.rb, line 7 def count(*value) if value.empty? super() else values.count(*value) end end
Access to a hash as if it were an OpenStruct.
h = {:a=>1, :b=>2} h.data.a #=> 1 h.data.b #=> 2 h.data.c #=> nil h.data.c = 3 h.data.c #=> 3 h.data.a? #=> true h.data.d? #=> false
TODO: Is there a better name for `data` –perhaps `open`?
TODO: Is this method really worth having?
Returns [Functor].
# File lib/core/facets/hash/data.rb, line 25 def data this = self Functor.new do |op, *a| case op.to_s when /\=$/ op = op.to_s.chomp('=') this[op] = a.first when /\?$/ op = op.to_s.chomp('?') this.key?(op.to_s) || this.key?(op.to_sym) when /\!$/ op = op.to_s.chomp('!') this[op] # ??? else this[op.to_s] || this[op.to_sym] end end end
Any array values with one or no elements will be set to the element or nil.
h = { :a=>[1], :b=>[1,2], :c=>3, :d=>[] } h.dearray_singular_values #=> { :a=>1, :b=>[1,2], :c=>3, :d=>nil }
CREDIT: Trans
# File lib/core/facets/hash/dearray_values.rb, line 32 def dearray_singular_values h = {} each do |k,v| case v when Array h[k] = (v.size < 2) ? v[0] : v else h[k] = v end end h end
Any array values will be replaced with the first element of the array. Arrays with no elements will be set to nil.
h = { :a=>[1], :b=>[1,2], :c=>3, :d=>[] } h.dearray_values #=> { :a=>1, :b=>1, :c=>3, :d=>nil }
CREDIT: Trans
# File lib/core/facets/hash/dearray_values.rb, line 11 def dearray_values(index=0) h = {} each do |k,v| case v when Array h[k] = v[index] || v[-1] else h[k] = v end end h end
Same as Hash#merge but recursively merges sub-hashes.
# File lib/core/facets/hash/deep_merge.rb, line 5 def deep_merge(other) hash = self.dup other.each do |key, value| myval = self[key] if value.is_a?(Hash) && myval.is_a?(Hash) hash[key] = myval.deep_merge(value) else hash[key] = value end end hash end
Same as Hash#merge! but recursively merges sub-hashes.
# File lib/core/facets/hash/deep_merge.rb, line 20 def deep_merge!(other) other.each do |key, value| myval = self[key] if value.is_a?(Hash) && myval.is_a?(Hash) myval.deep_merge!(value) else self[key] = value end end self end
Rekey a hash and all sub-hashes:
deep_rekey() deep_rekey(from_key => to_key, ...) deep_rekey{|from_key| to_key} deep_rekey{|from_key, value| to_key}
If a key map is given, then the first key is changed to the second key.
foo = { :c=>{ :a=>1, :b=>2 } } foo.deep_rekey(:a=>'a') #=> { :c=>{ 'a'=>1, :b=>2 } } foo.deep_rekey(:b=>:x) #=> { :c=>{ :a =>1, :x=>2 } } foo.deep_rekey('foo'=>'bar') #=> { :c=>{ :a =>1, :b=>2 } }
If a block is given, converts all keys in the Hash accroding to the given block procedure.
foo = { :person=>{ :name=>'Gavin', :wife=>:Lisa } } foo.deep_rekey{ |k| k.to_s } #=> { "person"=>{ "name"=>"Gavin", "wife"=>:Lisa } } foo #=> { "person"=>{ "name"=>"Gavin", "wife"=>:Lisa } }
If no key map or block is given, then all keys are converted to Symbols.
Raises an ArgumentError if both a key_map
and a block are
given. If both are needed just call deep_rekey twice.
TODO: If `nil` is returned by block should the key be set to `nil` or the orignal key?
CREDIT: Trans, Gavin Kistner, Ryan Duryea
# File lib/core/facets/hash/deep_rekey.rb, line 37 def deep_rekey(key_map=nil, &block) recurse do |h| h.rekey(key_map, &block) end end
Synonym for #deep_rekey, but modifies the receiver in place (and returns it).
foo = { :person=>{ :name=>'Gavin', :wife=>:Lisa } } foo.deep_rekey!{ |k| k.to_s } #=> { "person"=>{ "name"=>"Gavin", "wife"=>:Lisa } } foo #=> { "person"=>{ "name"=>"Gavin", "wife"=>:Lisa } }
CREDIT: Trans, Gavin Kistner, Ryan Duryea
# File lib/core/facets/hash/deep_rekey.rb, line 51 def deep_rekey!(key_map=nil, &block) replace(deep_rekey(key_map, &block)) end
Inverse of delete_if.
h = { :a => 1, :b => 2, :c => 3 } r = h.delete_unless{|k,v| v == 1} r #=> { :a => 1 } h #=> { :a => 1 }
CREDIT: Daniel Schierbeck
# File lib/core/facets/hash/delete_unless.rb, line 12 def delete_unless #:yield: delete_if{ |key, value| ! yield(key, value) } end
Minor modification to Ruby's Hash#delete method allowing it to take multiple keys.
hsh = { :a => 1, :b => 2 } hsh.delete_values(1) hsh #=> { :b => 2 }
Returns a list of keys of the deleted entries.
CREDIT: Daniel Schierbeck
# File lib/core/facets/hash/delete_values.rb, line 14 def delete_values(*values) deleted_keys = [] keys.each do |key| if values.include?(fetch(key)) deleted_keys << key delete(key) end end deleted_keys end
Minor modification to Ruby's Hash#delete method allowing it to take multiple keys.
hsh = {:a=>1, :b=>2, :c=>3} a, b, c = hsh.delete_values_at(:a, :b, :c) [a, b, c] #=> [1, 2, 3] hsh #=> {}
CREDIT: Daniel Schierbeck
# File lib/core/facets/hash/delete_values.rb, line 37 def delete_values_at(*keys, &yld) keys.map{|key| delete(key, &yld) } end
Difference comparison of two hashes.
h1 = {:a=>1,:b=>2} h2 = {:a=>1,:b=>3} h1.diff(h2) #=> {:b=>2} h2.diff(h1) #=> {:b=>3}
# File lib/core/facets/hash/diff.rb, line 11 def diff(hash) h1 = self.dup.delete_if{ |k,v| hash[k] == v } h2 = hash.dup.delete_if{ |k,v| has_key?(k) } h1.merge(h2) end
Each with key is like each_pair but reverses the order the parameters to [value,key] instead of [key,value].
CREDIT: Trans
# File lib/core/facets/hash/each_with_key.rb, line 8 def each_with_key( &yld ) each_pair{ |k,v| yld.call(v,k) } end
Returns a new hash less the given keys.
# File lib/core/facets/hash/except.rb, line 4 def except(*less_keys) hash = dup less_keys.each{ |k| hash.delete(k) } hash end
Replaces hash with new hash less the given keys.
h = {:a=>1, :b=>2, :c=>3} h.except!(:a) #=> {:b=>2,:c=>3} h #=> {:b=>2,:c=>3}
Returns the hash.
# File lib/core/facets/hash/except.rb, line 17 def except!(*rejected) rejected.each{ |k| delete(k) } self end
By default, only instances of Hash itself are extractable. Subclasses of Hash may implement this method and return true to declare themselves as extractable. If a Hash is extractable, Array#extract_options! pops it from the Array when it is the last element of the Array.
# File lib/core/facets/array/extract_options.rb, line 7 def extractable_options? instance_of?(Hash) end
Similar to Hash#fetch but supports nested lookup and is `nil` safe.
{}.fetch_nested('anything','at','all') #=> nil h = {'hello'=>{'world'=>42}} h.fetch_nested(*['hello','world']) #=> 42
CREDIT: T. Yamada and Sean Mackesey
# File lib/core/facets/hash/fetch_nested.rb, line 12 def fetch_nested(*keys) begin keys.reduce(self){|accum, k| accum.fetch(k)} rescue (RUBY_VERSION<'1.9' ? IndexError : KeyError) block_given? ? yield(*keys) : nil end end
In place version of graph.
NOTE: #graph! is only useful for Hash. It is not generally applicable to Enumerable.
# File lib/core/facets/hash/graph.rb, line 10 def graph!(&yld) replace(graph(&yld)) end
As with store but only if the key isn't already in the hash.
TODO: Would store? be a better name?
CREDIT: Trans
# File lib/core/facets/hash/insert.rb, line 10 def insert(name, value) if key?(name) false else store(name,value) true end end
Create a “true” inverse hash by storing mutliple values in Arrays.
h = {"a"=>3, "b"=>3, "c"=>3, "d"=>2, "e"=>9, "f"=>3, "g"=>9} h.invert #=> {2=>"d", 3=>"f", 9=>"g"} h.inverse #=> {2=>"d", 3=>["f", "c", "b", "a"], 9=>["g", "e"]} h.inverse.inverse #=> {"a"=>3, "b"=>3, "c"=>3, "d"=>2, "e"=>9, "f"=>3, "g"=>9}
Of course the inverse of the inverse should be the same.
(h.inverse.inverse == h) #=> true
CREDIT: Tilo Sloboda
# File lib/core/facets/hash/inverse.rb, line 17 def inverse i = Hash.new self.each_pair{ |k,v| if (Array === v) v.each{ |x| i[x] = ( i.has_key?(x) ? [k,i[x]].flatten : k ) } else i[v] = ( i.has_key?(v) ? [k,i[v]].flatten : k ) end } return i end
Like Array#join but specialized to Hash.
NOTE: Without Ruby 1.9 this would be difficult to rely on becuase hashes did not have a strict order.
CREDIT: Mauricio Fernandez
# File lib/core/facets/hash/join.rb, line 10 def join(pair_divider='', elem_divider=nil) elem_divider ||= pair_divider s = [] each{ |k,v| s << "#{k}#{pair_divider}#{v}" } s.join(elem_divider) end
Returns true or false whether the hash contains the given keys.
h = { :a => 1, :b => 2 } h.has_keys?( :a ) #=> true h.has_keys?( :c ) #=> false
CREDIT: Trans
# File lib/core/facets/hash/keys.rb, line 12 def keys?(*check_keys) unknown_keys = check_keys - self.keys return unknown_keys.empty? end
# File lib/core/facets/object/object_state.rb, line 61 def object_state(data=nil) data ? replace(data) : dup end
Returns true if the hash contains only the given keys, otherwise false.
h = { :a => 1, :b => 2 } h.has_only_keys?( :a, :b ) #=> true h.has_only_keys?( :a ) #=> false
CREDIT: Trans
# File lib/core/facets/hash/keys.rb, line 28 def only_keys?(*check_keys) unknown_keys = self.keys - check_keys return unknown_keys.empty? end
Apply a block to hash, and recursively apply that block to each sub-hash or
types
.
h = {:a=>1, :b=>{:b1=>1, :b2=>2}} g = h.recurse{|h| h.inject({}){|h,(k,v)| h[k.to_s] = v; h} } g #=> {"a"=>1, "b"=>{"b1"=>1, "b2"=>2}}
# File lib/core/facets/hash/recurse.rb, line 10 def recurse(*types, &block) types = [self.class] if types.empty? h = inject({}) do |hash, (key, value)| case value when *types hash[key] = value.recurse(*types, &block) else hash[key] = value end hash end yield h end
In place form of recurse.
# File lib/core/facets/hash/recurse.rb, line 25 def recurse!(&block) replace(recurse(&block)) end
Apply a block to a hash, and recursively apply that block to each sub-hash:
h = {:a=>1, :b=>{:x=>1, :y=>2}} h.recursively.map{ |k,v| [k.to_s, v] } #=> [["a", 1], ["b", [["y", 2], ["x", 1]]]]
The recursive iteration can be treated separately from the non-recursive iteration by passing a block to the recursive method:
h = {:a=>1, :b=>{:x=>1, :y=>2}} h.recursively{ |k,v| [k.to_s, v] }.map{ |k,v| [k.to_s, v.to_s] } #=> [["a", "1"], ["b", [["y", "2"], ["x", "1"]]]]
# File lib/core/facets/hash/recursively.rb, line 20 def recursively(*types, &block) Recursor.new(self, *types, &block) end
Rekey a hash:
rekey() rekey(from_key => to_key, ...) rekey{|from_key| to_key} rekey{|from_key, value| to_key}
If a key map is given, then the first key is changed to the second key.
foo = { :a=>1, :b=>2 } foo.rekey(:a=>'a') #=> { 'a'=>1, :b=>2 } foo.rekey(:b=>:x) #=> { :a =>1, :x=>2 } foo.rekey('foo'=>'bar') #=> { :a =>1, :b=>2 }
If a block is given, converts all keys in the Hash accroding to the given block procedure.
foo = { :name=>'Gavin', :wife=>:Lisa } foo.rekey{ |k| k.to_s } #=> { "name"=>"Gavin", "wife"=>:Lisa } foo #=> { :name =>"Gavin", :wife=>:Lisa }
If no key map or block is given, then all keys are converted to Symbols.
Raises an ArgumentError if both a key_map
and a block are
given. If both are needed just call rekey twice.
TODO: If `nil` is returned by block should the key be set to `nil` or the orignal key?
CREDIT: Trans, Gavin Kistner
# File lib/core/facets/hash/rekey.rb, line 34 def rekey(key_map=nil, &block) raise ArgumentError, "argument or block" if key_map && block if !(key_map or block) block = lambda{|k| k.to_sym} end if block hash = dup.clear if block.arity.abs == 1 each_pair do |k, v| hash[block[k]] = v #hash[block[k] || k] = v end else each_pair do |k, v| hash[block[k,v]] = v #hash[block[k,v] || k] = v end end else #hash = dup.clear # to keep default_proc #(keys - key_map.keys).each do |key| # hash[key] = self[key] #end #key_map.each do |from, to| # hash[to] = self[from] if key?(from) #end hash = dup # to keep default_proc key_map.each_pair do |from, to| hash[to] = hash.delete(from) if hash.key?(from) end end hash end
Synonym for #rekey, but modifies the receiver in place (and returns it).
foo = { :name=>'Gavin', :wife=>:Lisa } foo.rekey!{ |k| k.to_s } #=> { "name"=>"Gavin", "wife"=>:Lisa } foo #=> { "name"=>"Gavin", "wife"=>:Lisa }
CREDIT: Trans, Gavin Kistner
# File lib/core/facets/hash/rekey.rb, line 77 def rekey!(key_map=nil, &block) replace(rekey(key_map, &block)) end
Replaces hash with new hash less the given keys. This returns the hash of keys removed.
h = {:a=>1, :b=>2, :c=>3} h.except!(:a) #=> {:a=>1} h #=> {:b=>2,:c=>3}
Returns a Hash of the removed pairs.
# File lib/core/facets/hash/except.rb, line 30 def remove!(*rejected) removed = {} rejected.each{ |k| removed[k] = delete(k) } removed end
Same as update_each, but deletes the key element first.
CREDIT: Trans
# File lib/core/facets/hash/replace_each.rb, line 7 def replace_each # :yield: dup.each do |k,v| delete(k) update(yield(k,v)) end self end
Generates a new hash where the values are the result of the passed in block. The block takes both the key and value of the current entry as arguments.
hash = { a: 1, b: 2 } hash.revalue { |v| v + 1 } # => { a: 2, b: 3 }
Returns [Hash].
Credit: Sean Mackesey
# File lib/core/facets/hash/revalue.rb, line 14 def revalue(val_map=nil, &block) raise ArgumentError, "argument or block, not both" if val_map && block if !(val_map or block) raise ArgumentError, "must provide Hash arguments or a block" #block = lambda{|v| v.to_s} end if block hash = dup.clear # to keep default_proc if block.arity.abs == 1 each_pair do |k, v| hash[k] = block[v] #hash[k] = block[v] || v end else each_pair do |k, v| hash[k] = block[k,v] #hash[k] = block[k,v] || v end end else hash = dup.clear # to keep default_proc each do |k,v| if val_map.key?(v) hash[k] = val_map[v] else hash[k] = v end end end hash end
The in-place version of #revalue.
hash = { a: 1, b: 2 } hash.revalue! { |v| v + 1 } hash # => { a: 2, b: 3 }
Returns [Hash].
Credit: Sean Mackesey
# File lib/core/facets/hash/revalue.rb, line 57 def revalue!(val_map=nil, &block) replace(revalue(val_map, &block)) end
Allows for reverse merging where its the keys in the calling hash that wins
over those in the other_hash
. This is particularly useful for
initializing an incoming option hash with default values:
def setup(options = {}) options.reverse_merge! :size => 25, :velocity => 10 end
The default :size and :velocity is only set if the options
passed in doesn't already have those keys set.
# File lib/core/facets/hash/reverse_merge.rb, line 15 def reverse_merge(other) other.merge(self) end
Inplace form of reverse_merge.
# File lib/core/facets/hash/reverse_merge.rb, line 21 def reverse_merge!(other) replace(reverse_merge(other)) end
# File lib/standard/facets/shellwords.rb, line 110 def shelljoin shellwords.shelljoin end
# File lib/standard/facets/shellwords.rb, line 90 def shellwords argv = [] each do |f,v| m = f.to_s.size == 1 ? '-' : '--' case v when false, nil when Array v.each do |e| argv << %Q[#{m}#{f}="#{e}"] end when true argv << %Q[#{m}#{f}] else argv << %Q[#{m}#{f}="#{v}"] end end argv end
Returns a new hash with only the given keys.
h = {:a=>1, :b=>2, :c=>3} h.slice(:a, :c) #=> {:a=>1, :c=>3} h.slice(:a, :d) #=> {:a=>1}
# File lib/core/facets/hash/slice.rb, line 9 def slice(*keep_keys) if block_given? each do |k, v| keep_keys << k if yield(k, v) end end hash = {} keep_keys.each do |key| hash[key] = fetch(key) if key?(key) end hash end
Replaces hash with a new hash having only the given keys. This return the hash of keys removed.
h = {:a=>1, :b=>2} h.slice!(:a) #=> {:b=>2} h #=> {:a=>1}
Returns a Hash of the removed pairs.
# File lib/core/facets/hash/slice.rb, line 31 def slice!(*keep_keys) if block_given? each do |k, v| keep_keys << k if yield(k, v) end end rejected = keys - keep_keys removed = {} rejected.each{ |k| removed[k] = delete(k) } removed end
Return a new hash with all keys converted to strings. Converts all keys in
the Hash to Strings, returning a new Hash. With a select
block, limits
conversion to only a certain selection of keys.
foo = { :name=>'Gavin', :wife=>:Lisa } foo.stringify_keys #=> { "name"=>"Gavin", "wife"=>:Lisa } foo #=> { :name =>"Gavin", :wife=>:Lisa }
This method is considered archaic. Use rekey instead.
# File lib/core/facets/hash/symbolize_keys.rb, line 61 def stringify_keys(&select) dup.stringify_keys!(&select) end
Destructively convert all keys to strings. This is the same as #stringify_keys, but modifies
the receiver in place and returns it. With a select
block,
limits conversion to only certain keys.
foo = { :name=>'Gavin', :wife=>:Lisa } foo.stringify_keys! #=> { "name"=>"Gavin", "wife"=>:Lisa } foo #=> { "name"=>"Gavin", "wife"=>:Lisa }
This method is considered archaic. Use rekey instead.
# File lib/core/facets/hash/symbolize_keys.rb, line 76 def stringify_keys!(&select) if select keys.each do |key| if select[key] self[key.to_s] = delete(key) end end else keys.each do |key| self[key.to_s] = delete(key) end end self end
Take a subset of the hash, based on keys given or a block that evaluates to true for each hash key.
{'a'=>1, 'b'=>2}.subset('a') #=> {'a'=>1} {'a'=>1, 'b'=>2}.subset{|k| k == 'a'} #=> {'a'=>1}
CREDIT: Alexey Petrushin
# File lib/core/facets/hash/subset.rb, line 10 def subset(*keys, &block) h = {} if block raise ArgumentError, "subset arguments and block are exclusive parameters" unless keys.empty? each do |k, v| h[k] = v if block.call(k) end else each do |k, v| h[k] = v if keys.include?(k) end end return h end
Swap the values of a pair of keys in place.
{:a=>1,:b=>2}.swap!(:a,:b) #=> {:a=>2,:b=>1}
CREDIT: Gavin Sinclair
# File lib/core/facets/hash/swap.rb, line 9 def swap!(key1, key2) tmp = self[key1] self[key1] = self[key2] self[key2] = tmp self end
Return a new hash with all keys converted to symbols. With a
select
block, limits conversion to only a certain selection of
keys.
foo = { :name=>'Gavin', 'wife'=>:Lisa } foo.symbolize_keys #=> { :name=>"Gavin", :wife=>:Lisa } foo #=> { :name =>"Gavin", "wife"=>:Lisa }
If the key does not respond to to_sym, then to_s will be used first.
For a more versatile method, see rekey instead.
# File lib/core/facets/hash/symbolize_keys.rb, line 18 def symbolize_keys(&select) dup.symbolize_keys!(&select) end
Destructively convert all keys to symbols. This is the same as #symbolize_keys, but modifies
the receiver in place and returns it. With a select
block,
limits conversion to only selected keys.
foo = { 'name'=>'Gavin', 'wife'=>:Lisa } foo.symbolize_keys! #=> { :name=>"Gavin", :wife=>:Lisa } foo #=> { :name=>"Gavin", :wife=>:Lisa }
If the key does not respond to to_sym, then to_s will be used first.
For a more versatile method, see rekey instead.
# File lib/core/facets/hash/symbolize_keys.rb, line 34 def symbolize_keys!(&select) if select keys.each do |key| if select[key] new_key = (key.to_sym rescue key.to_s.to_sym) self[new_key] = delete(key) end end else keys.each do |key| new_key = (key.to_sym rescue key.to_s.to_sym) self[new_key] = delete(key) end end self end
Boolean conversion for not empty?
# File lib/core/facets/boolean.rb, line 117 def to_b ! self.empty? end
Convert a hash into a module.
{:a=>1, :b=>2}.to_mod
Can take a block accepting key, value pairs which will be evaluated in the context of the module.
h = {:a=>1, :b=>2} m = h.to_mod{ |k,v| module_function k } m.a #=> 1 m.b #=> 2
CREDIT: Jay Fields
# File lib/core/facets/hash/to_mod.rb, line 20 def to_mod(&block) hash = self Module.new do hash.each do |key, value| define_method key do value #.to_object end instance_exec(key, value, &block) if block end end end
Turns a hash into a generic object using an OpenStruct.
o = {'a' => 1}.to_ostruct o.a #=> 1
# File lib/standard/facets/ostruct/to_ostruct.rb, line 16 def to_ostruct OpenStruct.new(self) end
Like `#to_ostruct` but recusively objectifies all hash elements as well.
o = {'a' => { 'b' => 1 }}.to_ostruct_recurse o.a.b #=> 1
The `exclude` parameter is used internally to prevent infinite recursion and is not intended to be utilized by the end-user. But for more advance use, if there is a particular subhash you would like to prevent from being converted to an OpoenStruct then include it in the `exclude` hash referencing itself. e.g.
h = { 'a' => { 'b' => 1 } } o = h.to_ostruct_recurse( { h['a'] => h['a'] } ) o.a['b'] #=> 1
CREDIT: Alison Rowland, Jamie Macey, Mat Schaffer
# File lib/standard/facets/ostruct/to_ostruct.rb, line 48 def to_ostruct_recurse(exclude={}) return exclude[self] if exclude.key?( self ) o = exclude[self] = OpenStruct.new h = self.dup each_pair do |k,v| h[k] = v.to_ostruct_recurse( exclude ) if v.respond_to?(:to_ostruct_recurse) end o.merge!(h) end
Constructs a Proc object from a hash such that the parameter of the Proc is assigned the hash keys as attributes.
c = Class.new do attr_accessor :a end h = {:a => 1} o = c.new h.to_proc.call(o) o.a #=> 1
If response
is set to true
, then assignment will
only occur if receiver responds_to? the writer method.
CREDIT: Trans
# File lib/core/facets/hash/to_proc.rb, line 20 def to_proc(response=false) if response lambda do |o| self.each do |k,v| ke = "#{k}=" o.__send__(ke, v) if respond_to?(ke) end end else lambda do |o| self.each do |k,v| ke = "#{k}=" o.__send__(ke, v) end end end end
A method to convert a Hash into a Struct.
h = {:name=>"Dan","age"=>33,"rank"=>"SrA","grade"=>"E4"} s = h.to_struct("Foo")
TODO: Is this robust enough considerd hashes aren't ordered?
CREDIT: Daniel Berger
# File lib/core/facets/hash/to_struct.rb, line 12 def to_struct(struct_name) Struct.new(struct_name,*keys).new(*values) end
Returns a new hash created by traversing the hash and its subhashes, executing the given block on the key and value. The block should return a 2-element array of the form +[key, value]+.
h = {"A"=>"A", "B"=>"B", "C"=>{"X"=>"X"}} g = h.traverse{ |k,v| [k.downcase, v] } g #=> {"a"=>"A", "b"=>"B", "c"=>{"x"=>"X"}}
NOTE: #traverse is the same as `recursive.graph` and might be deprecated in the future (if it ever works!)
CREDIT: Trans
# File lib/core/facets/hash/traverse.rb, line 18 def traverse(&block) inject({}) do |h,(k,v)| if Hash === v v = v.traverse(&block) elsif v.respond_to?(:to_hash) v = v.to_hash.traverse(&block) end nk, nv = block.call(k,v) h[nk] = nv h end end
In place version of traverse, which traverses the hash and its subhashes, executing the given block on the key and value.
h = { "A"=>"A", "B"=>"B" } h.traverse!{ |k,v| [k.downcase, v] } h #=> { "a"=>"A", "b"=>"B" }
CREDIT: Trans
# File lib/core/facets/hash/traverse.rb, line 42 def traverse!(&block) replace(traverse(&block)) end
Iterates through each pair and updates the hash in place. This is formally equivalent to mash! But does not use mash to accomplish the task. Hence update_each is probably a touch faster.
CREDIT: Trans
# File lib/core/facets/hash/update_each.rb, line 10 def update_each # :yield: dup.each do |k,v| update(yield(k,v)) end self end
Iterate over hash updating just the keys.
h = {:a=>1, :b=>2} h.update_keys{ |k| "#{k}!" } h #=> { "a!"=>1, "b!"=>2 }
@author Trans @author Evgeniy Dolzhenko (bug fix)
# File lib/core/facets/hash/update_keys.rb, line 12 def update_keys #:yield: if block_given? keys.each { |old_key| store(yield(old_key), delete(old_key)) } else to_enum(:update_keys) end end
Iterate over hash updating just the values.
h = {:a=>1, :b=>2} h.update_values{ |v| v + 1 } h #=> { :a=>2, :b=>3 }
CREDIT: Trans
# File lib/core/facets/hash/update_values.rb, line 11 def update_values #:yield: if block_given? each{ |k,v| store(k, yield(v)) } else to_enum(:update_values) end end
Weave is a very unique hash operator. It is designed to merge to complex hashes in according to sensible, regular pattern. The effect is akin to inheritance.
Two hashes are weaved together to produce a new hash. The two hashes need to be compatible according to the following rules for each node: …
hash, hash => hash (recursive +) hash, array => error hash, value => error array, hash => error array, array => array + array array, value => array << value value, hash => error value, array => array.unshift(valueB) value1, value2 => value2
Here is a basic example:
h1 = { :a => 1, :b => [ 1 ], :c => { :x => 1 } } h2 = { :a => 2, :b => [ 2 ], :c => { :x => 2 } } h1.weave(h2) #=> {:b=>[1, 2], :c=>{:x=>2}, :a=>2}
Weave follows the most expected pattern of unifying two complex hashes. It is especially useful for implementing overridable configuration schemes.
CREDIT: Thomas Sawyer
# File lib/core/facets/hash/weave.rb, line 35 def weave(h) raise ArgumentError, "Hash expected" unless h.kind_of?(Hash) s = self.clone h.each { |k,node| node_is_hash = node.kind_of?(Hash) node_is_array = node.kind_of?(Array) if s.has_key?(k) self_node_is_hash = s[k].kind_of?(Hash) self_node_is_array = s[k].kind_of?(Array) if self_node_is_hash if node_is_hash s[k] = s[k].weave(node) elsif node_is_array raise ArgumentError, 'Incompatible hash addition' #self[k] = node else raise ArgumentError, 'Incompatible hash addition' #self[k] = node end elsif self_node_is_array if node_is_hash raise ArgumentError, 'Incompatible hash addition' #self[k] = node elsif node_is_array s[k] += node else s[k] << node end else if node_is_hash raise ArgumentError, 'Incompatible hash addition' #self[k] = node elsif node_is_array s[k].unshift( node ) else s[k] = node end end else s[k] = node end } s end
Operator for reverse_merge.
CREDIT: Trans
# File lib/core/facets/hash/op_or.rb, line 7 def |(other) other.merge(self) end