class Hash

Hash extensions

Public Instance Methods

merge_recurse(other_hash) click to toggle source

Return a new hash 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; if the values are both hashes, then this method recurses.

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: 'b', c: {d: 'e'}}
h2 = {a: 'B', c: {d: 'E'}}
h1.merge_recurse(h2)
#=> {a: 'B', c: {d: 'E'}}
# File lib/sixarm_ruby_ramp/hash.rb, line 47
def merge_recurse(other_hash)
  merge(other_hash){|key, oldval, newval|
    if oldval.is_a?(Hash) && newval.is_a?(Hash)
      oldval.merge_recurse(newval)
    else
      newval
    end
  }
end
pivot(direction='keys',&block) click to toggle source

Hash#pivot aggregates values for a hash of hashes, for example to calculate subtotals and groups.

Suppose you have data arranged by companies, roles, and headcounts.

  data = {
  "Apple"     => {"Accountants" => 11, "Designers" => 22, "Developers" => 33},
  "Goggle"    => {"Accountants" => 44, "Designers" => 55, "Developers" => 66},
  "Microsoft" => {"Accountants" => 77, "Designers" => 88, "Developers" => 99},
}

To calculate each company's total headcount, you pivot up, then sum:

data.pivot(:up,&:sum)
=> {
 "Apple"=>66,
 "Goggle"=>165,
 "Microsoft"=>264
}

To calculate each role's total headcount, you pivot down, then sum:

 data.pivot(:down,&:sum)
 => {
  "Accountants"=>132,
  "Designers"=>165,
  "Developers"=>198
 }

Generic example:
 h={
  "a"=>{"x"=>1,"y"=>2,"z"=>3},
  "b"=>{"x"=>4,"y"=>5,"z"=>6},
  "c"=>{"x"=>7,"y"=>8,"z"=>9},
 }
 h.pivot(:keys) => {"a"=>[1,2,3],"b"=>[4,5,6],"c"=>[7,8,9]}
 h.pivot(:vals) => {"x"=>[1,4,7],"y"=>[2,5,8],"z"=>[3,6,9]}

Calculating subtotals

The pivot method is especially useful for calculating subtotals.

@example

r = h.pivot(:keys)
r['a'].sum => 6
r['b'].sum => 15
r['c'].sum => 24

@example

r=h.pivot(:vals)
r['x'].sum => 12
r['y'].sum => 15
r['z'].sum => 18

Block customization

You can provide a block that will be called for the pivot items.

@example

h.pivot(:keys){|items| items.max } => {"a"=>3,"b"=>6,"c"=>9}
h.pivot(:keys){|items| items.join("/") } => {"a"=>"1/2/3","b"=>"4/5/6","c"=>"7/8/9"}
h.pivot(:keys){|items| items.inject{|sum,x| sum+=x } } => {"a"=>6,"b"=>15,"c"=>24}

@example

h.pivot(:vals){|items| items.max } => {"a"=>7,"b"=>8,"c"=>9}
h.pivot(:vals){|items| items.join("-") } => {"a"=>"1-4-7","b"=>"2-5-8","c"=>"3-6-9"}
h.pivot(:vals){|items| items.inject{|sum,x| sum+=x } } => {"a"=>12,"b"=>15,"c"=>18}
# File lib/sixarm_ruby_ramp/hash.rb, line 125
def pivot(direction='keys',&block)
  a=self.class.new
  direction=direction.to_s
  up=pivot_direction_up?(direction)
  each_pair{|k1,v1|
    v1.each_pair{|k2,v2|
      k = up ? k1 : k2
      a[k]=[] if (a[k]==nil or a[k]=={})
      a[k]<<(v2)
    }
  }
  if block
    a.each_pair{|key,val| a[key]=block.call(val)}
  end
  a
end
size?() click to toggle source

Does the hash contain any items?

@return [Boolean] true if size > 0

# File lib/sixarm_ruby_ramp/hash.rb, line 14
def size?
  size>0
end
sort_by_keys() click to toggle source

Sort the hsh items by the keys.

@return [Hash] a new hash sorted by the keys

@example

h = {"c" => "cherry", "b" => "banana", "a" =>"apple" }
h.sort_keys => {"a" => "apple", "b" => "banana", "c" => "cherry"}
# File lib/sixarm_ruby_ramp/hash.rb, line 26
def sort_by_keys
  Hash[sort]
end

Protected Instance Methods

pivot_direction_up?(direction_name) click to toggle source
# File lib/sixarm_ruby_ramp/hash.rb, line 144
def pivot_direction_up?(direction_name)
  case direction_name.to_s
  when 'key','keys','up','left','out' then return true
  when 'val','vals','down','right','in' then return false
  else raise ArgumentError.new('Pivot direction must be either: key/keys/up/left/out or val/vals/down/right/in')
  end
end