require 'yaml' class Hash # Return true if size > 0 def size? size>0 end # Calls block once for each key in hsh, passing the key and value to the block as a two-element array. # # The keys are sorted. def each_sort keys.sort.each{|key| yield key,self[key] } end # Calls block once for each key in hsh, # passing the key as a parameter, # and updating it in place. # # ==Example # h = { "a" => "b", "c" => "d" } # h.each_key! {|key| key.upcase } # h => { "A" => "b", "C" => "d" } # # Return self. def each_key! each_pair{|key,value| key2=yield(key) if key===key2 #nop else self.delete(key) self[key2]=value end } end # Calls block once for each key in hsh, # passing the key and value as parameters, # and updated them in place. # # ==Example # h = { "a" => "b", "c" => "d" } # h.each_pair! {|key,value| key.upcase, value.upcase } # h => { "A" => "B", "C" => "D" } # # Return self. def each_pair! each_pair{|key,value| key2,value2=yield(key,value) if key===key2 if value===value2 #nop else self[key]=value2 end else self.delete(key) self[key2]=value2 end } return self end # Calls block once for each key in hsh, # passing the value as a parameter, # and updating it in place. # # ==Example # h = { "a" => "b", "c" => "d" } # h.each_value! {|value| value.upcase } # h => { "a" => "B", "c" => "d" } # # Return self. def each_value! each_pair{|key,value| value2=yield(value) if value===value2 #nop else self[key]=yield(value) end } return self end # Calls block once for each key-value pair in hsh, # passing the key and value as paramters to the block. # # ==Example # h = {"a"=>"b", "c"=>"d", "e"=>"f" } # h.map_pair{|key,value| key+value } # => ["ab","cd","ef"] def map_pair keys.map{|key| yield key, self[key] } end # Hash#to_yaml with sort # # From http://snippets.dzone.com/tag/yaml def to_yaml_sort( opts = {} ) YAML::quick_emit( object_id, opts ) do |out| out.map( taguri, to_yaml_style ) do |map| sort.each do |k, v| # <-- here's my addition (the 'sort') map.add( k, v ) end end end end # Hash#pivot aggregates values for a hash of hashes, # for example to calculate subtotals and groups. # # 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. # # ==Examples # 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} # # ==Examples # 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} def pivot(direction='keys',&b) 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_given? a.each_pair{|k,v| a[k]=(yield v)} end a end protected def pivot_direction_up?(direction_name) case direction_name when 'key','keys','up','left','out' then return true when 'val','vals','down','right','in' then return false else raise 'Pivot direction must be either: up/left/out or down/right/in' end end end