Class: Hash

Inherits:
Object show all
Includes:
Random::HashExtensions, URI::Hash
Defined in:
lib/standard/facets/uri.rb,
lib/core/facets/boolean.rb,
lib/core/facets/hash/at.rb,
lib/core/facets/hash/zip.rb,
lib/core/facets/hash/data.rb,
lib/core/facets/hash/diff.rb,
lib/core/facets/hash/join.rb,
lib/core/facets/hash/keys.rb,
lib/core/facets/hash/swap.rb,
lib/core/facets/hash/alias.rb,
lib/core/facets/hash/count.rb,
lib/core/facets/hash/graph.rb,
lib/core/facets/hash/op_or.rb,
lib/core/facets/hash/rekey.rb,
lib/core/facets/hash/slice.rb,
lib/core/facets/hash/weave.rb,
lib/core/facets/hash/except.rb,
lib/core/facets/hash/insert.rb,
lib/core/facets/hash/op_add.rb,
lib/core/facets/hash/op_and.rb,
lib/core/facets/hash/op_mul.rb,
lib/core/facets/hash/op_sub.rb,
lib/core/facets/hash/subset.rb,
lib/core/facets/hash/to_mod.rb,
lib/core/facets/hash/autonew.rb,
lib/core/facets/hash/collate.rb,
lib/core/facets/hash/inverse.rb,
lib/core/facets/hash/op_push.rb,
lib/core/facets/hash/recurse.rb,
lib/core/facets/hash/revalue.rb,
lib/core/facets/hash/to_proc.rb,
lib/core/facets/kernel/blank.rb,
lib/core/facets/hash/new_with.rb,
lib/core/facets/hash/traverse.rb,
lib/core/facets/hash/delete_at.rb,
lib/core/facets/hash/to_struct.rb,
lib/core/facets/hash/deep_merge.rb,
lib/core/facets/hash/deep_rekey.rb,
lib/core/facets/hash/argumentize.rb,
lib/core/facets/hash/recursively.rb,
lib/core/facets/hash/update_each.rb,
lib/core/facets/hash/update_keys.rb,
lib/core/facets/hash/fetch_nested.rb,
lib/core/facets/hash/replace_each.rb,
lib/core/facets/hash/delete_unless.rb,
lib/core/facets/hash/delete_values.rb,
lib/core/facets/hash/each_with_key.rb,
lib/core/facets/hash/reverse_merge.rb,
lib/core/facets/hash/update_values.rb,
lib/core/facets/hash/dearray_values.rb,
lib/core/facets/hash/symbolize_keys.rb,
lib/core/facets/object/object_state.rb,
lib/core/facets/array/extract_options.rb,
lib/standard/facets/random.rb,
lib/standard/facets/shellwords.rb,
lib/standard/facets/ostruct/to_ostruct.rb

Overview

:nodoc:

Direct Known Subclasses

Multiton::InstanceMutex

Defined Under Namespace

Classes: Recursor

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Random::HashExtensions

#rand_key, #rand_key!, #rand_pair, #rand_pair!, #rand_value, #rand_value!, #shuffle, #shuffle!

Methods included from URI::Hash

#to_uri

Class Method Details

.autonew(*args) ⇒ Object

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



18
19
20
21
# 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

.new_withObject

Instantiate a new hash with a default value determined by the block.

Hash.new_with{ [] }

CREDIT: Pit Capitan



10
11
12
# File 'lib/core/facets/hash/new_with.rb', line 10

def self.new_with #:yield:
  new { |h, k| h[k] = yield }
end

.zip(keys, values) ⇒ Object

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



11
12
13
14
15
# 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

Instance Method Details

#&(other) ⇒ Object

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



19
20
21
22
23
24
25
26
27
28
29
30
# 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

#*(other) ⇒ Object

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



13
14
15
# File 'lib/core/facets/hash/op_mul.rb', line 13

def *(other)
  other.merge(self)
end

#+(other) ⇒ Object

Operator for #merge.

CREDIT: Trans



7
8
9
# File 'lib/core/facets/hash/op_add.rb', line 7

def +(other)
  merge(other)
end

#-(other) ⇒ Object

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)



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 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

#<<(other) ⇒ Object

Can be used like update, or passed as two-element [key,value] array.

CREDIT: Trans



8
9
10
11
12
13
14
15
# 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

#alias!(newkey, oldkey) ⇒ Object

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.



25
26
27
28
# File 'lib/core/facets/hash/alias.rb', line 25

def alias!(newkey, oldkey)
  self[newkey] = self[oldkey] if self.has_key?(oldkey)
  self
end

#argumentize(args_field = nil) ⇒ Object

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] } ]


16
17
18
19
20
21
22
23
24
25
# 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

#collate(*others) ⇒ Object

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)

  • Tilo Sloboda (bug fixes)

  • Gavin Kistner (original)



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 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

#collate!(other_hash) ⇒ Object

The same as #collate, but modifies the receiver in place.



43
44
45
46
# File 'lib/core/facets/hash/collate.rb', line 43

def collate!(other_hash)
  result = collate(other_hash)
  replace(result)
end

#count(*value) ⇒ Object

Like Enumerable#count, but can count hash values.

{:A=>1, :B=>1}.count(1)  #=> 2


7
8
9
10
11
12
13
# File 'lib/core/facets/hash/count.rb', line 7

def count(*value)
  if value.empty?
    super()
  else
    values.count(*value)
  end
end

#dataObject

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].



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 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

#dearray_singular_valuesObject

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



32
33
34
35
36
37
38
39
40
41
42
43
# 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

#dearray_values(index = 0) ⇒ Object

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



11
12
13
14
15
16
17
18
19
20
21
22
# 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

#deep_merge(other) ⇒ Object

Same as Hash#merge but recursively merges sub-hashes.



5
6
7
8
9
10
11
12
13
14
15
16
# 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

#deep_merge!(other) ⇒ Object

Same as Hash#merge! but recursively merges sub-hashes.



20
21
22
23
24
25
26
27
28
29
30
# 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

#deep_rekey(key_map = nil, &block) ⇒ Object

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



37
38
39
40
41
# 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

#deep_rekey!(key_map = nil, &block) ⇒ Object

Synonym for Hash#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



51
52
53
# File 'lib/core/facets/hash/deep_rekey.rb', line 51

def deep_rekey!(key_map=nil, &block)
  replace(deep_rekey(key_map, &block))
end

#delete_unlessObject

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



12
13
14
# File 'lib/core/facets/hash/delete_unless.rb', line 12

def delete_unless #:yield:
  delete_if{ |key, value| ! yield(key, value) }
end

#delete_values(*values) ⇒ Object

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



14
15
16
17
18
19
20
21
22
23
# 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

#delete_values_at(*keys, &yld) ⇒ Object

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



37
38
39
# File 'lib/core/facets/hash/delete_values.rb', line 37

def delete_values_at(*keys, &yld)
  keys.map{|key| delete(key, &yld) }
end

#diff(hash) ⇒ Object

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}


11
12
13
14
15
# 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(&yld) ⇒ Object

Each with key is like each_pair but reverses the order the parameters to [value,key] instead of [key,value].

CREDIT: Trans



8
9
10
# 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

#except(*less_keys) ⇒ Object

Returns a new hash less the given keys.



4
5
6
7
8
# File 'lib/core/facets/hash/except.rb', line 4

def except(*less_keys)
  hash = dup
  less_keys.each{ |k| hash.delete(k) }
  hash
end

#except!(*rejected) ⇒ Object

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.



17
18
19
20
# File 'lib/core/facets/hash/except.rb', line 17

def except!(*rejected)
  rejected.each{ |k| delete(k) }
  self
end

#extractable_options?Boolean

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.

Returns:

  • (Boolean)


7
8
9
# File 'lib/core/facets/array/extract_options.rb', line 7

def extractable_options?
  instance_of?(Hash)
end

#fetch_nested(*keys) ⇒ Object

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



12
13
14
15
16
17
18
# 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

#graph!(&yld) ⇒ Object Also known as: mash!

In place version of #graph.

NOTE: Hash#graph! is only useful for Hash. It is not generally applicable to Enumerable.



10
11
12
# File 'lib/core/facets/hash/graph.rb', line 10

def graph!(&yld)
  replace(graph(&yld))
end

#insert(name, value) ⇒ Object

As with #store but only if the key isn’t already in the hash.

TODO: Would #store? be a better name?

CREDIT: Trans



10
11
12
13
14
15
16
17
# File 'lib/core/facets/hash/insert.rb', line 10

def insert(name, value)
  if key?(name)
    false
  else
    store(name,value)
    true
  end
end

#inverseObject

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



17
18
19
20
21
22
23
24
25
26
27
# 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

#join(pair_divider = '', elem_divider = nil) ⇒ Object

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



10
11
12
13
14
15
# 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

#keys?(*check_keys) ⇒ Boolean Also known as: has_keys?

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

Returns:

  • (Boolean)


12
13
14
15
# File 'lib/core/facets/hash/keys.rb', line 12

def keys?(*check_keys)
  unknown_keys = check_keys - self.keys
  return unknown_keys.empty?
end

#object_state(data = nil) ⇒ Object



61
62
63
# File 'lib/core/facets/object/object_state.rb', line 61

def object_state(data=nil)
  data ? replace(data) : dup
end

#only_keys?(*check_keys) ⇒ Boolean Also known as: has_only_keys?

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

Returns:

  • (Boolean)


28
29
30
31
# 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

#recurse(*types) {|h| ... } ⇒ Object

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}}

Yields:

  • (h)


10
11
12
13
14
15
16
17
18
19
20
21
22
# 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

#recurse!(&block) ⇒ Object

In place form of #recurse.



25
26
27
# File 'lib/core/facets/hash/recurse.rb', line 25

def recurse!(&block)
  replace(recurse(&block))
end

#recursively(*types, &block) ⇒ Object

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"]]]]


20
21
22
# File 'lib/core/facets/hash/recursively.rb', line 20

def recursively(*types, &block)
  Recursor.new(self, *types, &block)
end

#rekey(key_map = nil, &block) ⇒ Object

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

Raises:

  • (ArgumentError)


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# 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

#rekey!(key_map = nil, &block) ⇒ Object

Synonym for Hash#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



77
78
79
# File 'lib/core/facets/hash/rekey.rb', line 77

def rekey!(key_map=nil, &block)
  replace(rekey(key_map, &block))
end

#remove!(*rejected) ⇒ Object

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.



30
31
32
33
34
# File 'lib/core/facets/hash/except.rb', line 30

def remove!(*rejected)
  removed = {}
  rejected.each{ |k| removed[k] = delete(k) }
  removed
end

#replace_eachObject

Same as #update_each, but deletes the key element first.

CREDIT: Trans



7
8
9
10
11
12
13
# 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

#revalue(val_map = nil, &block) ⇒ Object

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

Raises:

  • (ArgumentError)


14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 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

#revalue!(val_map = nil, &block) ⇒ Object

The in-place version of Hash#revalue.

hash = { a: 1, b: 2 }
hash.revalue! { |v| v + 1 }
hash  # => { a: 2, b: 3 }

Returns [Hash].

Credit: Sean Mackesey



57
58
59
# File 'lib/core/facets/hash/revalue.rb', line 57

def revalue!(val_map=nil, &block)
  replace(revalue(val_map, &block))
end

#reverse_merge(other) ⇒ Object

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.



15
16
17
# File 'lib/core/facets/hash/reverse_merge.rb', line 15

def reverse_merge(other)
  other.merge(self)
end

#reverse_merge!(other) ⇒ Object Also known as: reverse_update

Inplace form of #reverse_merge.



21
22
23
# File 'lib/core/facets/hash/reverse_merge.rb', line 21

def reverse_merge!(other)
  replace(reverse_merge(other))
end

#shelljoinObject



110
111
112
# File 'lib/standard/facets/shellwords.rb', line 110

def shelljoin
  shellwords.shelljoin
end

#shellwordsObject



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# 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 << %[#{m}#{f}="#{e}"]
      end
    when true
      argv << %[#{m}#{f}]
    else
      argv << %[#{m}#{f}="#{v}"]
    end
  end
  argv
end

#slice(*keep_keys) ⇒ Object

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}


9
10
11
12
13
14
15
16
17
18
19
# 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

  keep_keys.each_with_object({}) do |key, hash|
    hash[key] = fetch(key) if key?(key)
  end
end

#slice!(*keep_keys) ⇒ Object

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.



29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/core/facets/hash/slice.rb', line 29

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

#stringify_keys(&select) ⇒ Object

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.



61
62
63
# File 'lib/core/facets/hash/symbolize_keys.rb', line 61

def stringify_keys(&select)
  dup.stringify_keys!(&select)
end

#stringify_keys!(&select) ⇒ Object

Destructively convert all keys to strings. This is the same as Hash#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.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
# 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

#subset(*keys, &block) ⇒ Object

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



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 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!(key1, key2) ⇒ Object

Swap the values of a pair of keys in place.

{:a=>1,:b=>2}.swap!(:a,:b)  #=> {:a=>2,:b=>1}

CREDIT: Gavin Sinclair



9
10
11
12
13
14
# 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

#symbolize_keys(&select) ⇒ Object

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.



18
19
20
# File 'lib/core/facets/hash/symbolize_keys.rb', line 18

def symbolize_keys(&select)
  dup.symbolize_keys!(&select)
end

#symbolize_keys!(&select) ⇒ Object

Destructively convert all keys to symbols. This is the same as Hash#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.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 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

#to_bObject

Boolean conversion for not empty?



117
118
119
# File 'lib/core/facets/boolean.rb', line 117

def to_b
  ! self.empty?
end

#to_mod(&block) ⇒ Object

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 – TODO: Consider #to_obj? ++



20
21
22
23
24
25
26
27
28
29
30
# 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

#to_ostructObject

Turns a hash into a generic object using an OpenStruct.

o = {'a' => 1}.to_ostruct
o.a  #=> 1


16
17
18
# File 'lib/standard/facets/ostruct/to_ostruct.rb', line 16

def to_ostruct
  OpenStruct.new(self)
end

#to_ostruct_recurse(exclude = {}) ⇒ Object

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



48
49
50
51
52
53
54
55
56
# 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

#to_proc(response = false) ⇒ Object

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



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 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

#to_struct(struct_name) ⇒ Object

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



12
13
14
# File 'lib/core/facets/hash/to_struct.rb', line 12

def to_struct(struct_name)
  Struct.new(struct_name,*keys).new(*values)
end

#traverse(&block) ⇒ Object

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: Hash#traverse is the same as ‘recursive.graph` and might be deprecated in the future (if it ever works!)

CREDIT: Trans



18
19
20
21
22
23
24
25
26
27
28
29
# 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

#traverse!(&block) ⇒ Object

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



42
43
44
# File 'lib/core/facets/hash/traverse.rb', line 42

def traverse!(&block)
  replace(traverse(&block))
end

#update_eachObject

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



10
11
12
13
14
15
# 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

#update_keysObject

Iterate over hash updating just the keys.

h = {:a=>1, :b=>2}
h.update_keys{ |k| "#{k}!" }
h  #=> { "a!"=>1, "b!"=>2 }

Author:

  • Trans

  • Evgeniy Dolzhenko (bug fix)



12
13
14
15
16
17
18
# 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

#update_valuesObject

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



11
12
13
14
15
16
17
# 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(h) ⇒ Object

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

Raises:

  • (ArgumentError)


35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# 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

#|(other) ⇒ Object

Operator for #reverse_merge.

CREDIT: Trans



7
8
9
# File 'lib/core/facets/hash/op_or.rb', line 7

def |(other)
  other.merge(self)
end