Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Group hashes by keys and sum the values

I have an array of hashes:

[{"Vegetable"=>10}, {"Vegetable"=>5}, {"Dry Goods"=>3>}, {"Dry Goods"=>2}] 

I need to use inject here I think but I've really been struggling.

I want a new hash that reflects the sum of the previous hash's duplicate keys:

[{"Vegetable"=>15}, {"Dry Goods"=>5}] 

I'm in control of the code that outputs this hash so I can modify it if necessary. The results were mainly hashes because this could end up nested any number of levels deep and then it's easy to call flatten on the array but not flatten the keys/values of the hash too:

def recipe_pl(parent_percentage=nil)   ingredients.collect do |i|      recipe_total = i.recipe.recipeable.total_cost      recipe_percentage = i.ingredient_cost / recipe_total      if i.ingredientable.is_a?(Purchaseitem)       if parent_percentage.nil?         {i.ingredientable.plclass => recipe_percentage}       else         sub_percentage = recipe_percentage * parent_percentage         {i.ingredientable.plclass => sub_percentage}       end     else       i.ingredientable.recipe_pl(recipe_percentage)     end   end end  
like image 414
blastula Avatar asked Dec 15 '10 18:12

blastula


2 Answers

ar = [{"Vegetable"=>10}, {"Vegetable"=>5}, {"Dry Goods"=>3}, {"Dry Goods"=>2}] p ar.inject{|memo, el| memo.merge( el ){|k, old_v, new_v| old_v + new_v}} #=> {"Vegetable"=>15, "Dry Goods"=>5} 

Hash.merge with a block runs the block when it finds a duplicate; inject without a initial memo treats the first element of the array as memo, which is fine here.

like image 134
steenslag Avatar answered Sep 27 '22 17:09

steenslag


Simply use:

array = [{"Vegetable"=>10}, {"Vegetable"=>5}, {"Dry Goods"=>3}, {"Dry Goods"=>2}] array.inject{|a,b| a.merge(b){|_,x,y| x + y}} 
like image 26
user1922900 Avatar answered Sep 27 '22 18:09

user1922900