Regrouper les hachages par touches et additionner les valeurs

J’ai un tableau de hashes:

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

Je pense que je dois utiliser l’ inject ici, mais je me suis vraiment battu.

Je veux un nouveau hash qui reflète la sum des clés dupliquées du hash précédent:

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

Je contrôle le code qui génère ce hash pour pouvoir le modifier si nécessaire. Les résultats étaient principalement des hachages, car cela pourrait finir par imbriquer n’importe quel nombre de niveaux et il est alors facile d’appeler le tableau mais pas d’aplatir les clés / valeurs du hachage:

 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 

 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 avec un bloc exécute le bloc lorsqu’il trouve un doublon; inject sans memo initial traite le premier élément du tableau comme un memo , ce qui est bien ici.

Utilisez simplement:

 array = [{"Vegetable"=>10}, {"Vegetable"=>5}, {"Dry Goods"=>3}, {"Dry Goods"=>2}] array.inject{|a,b| a.merge(b){|_,x,y| x + y}} 
 ar = [{"Vegetable"=>10}, {"Vegetable"=>5}, {"Dry Goods"=>3}, {"Dry Goods"=>2}] 

Bien que la technique Hash.merge fonctionne bien, je pense qu’elle se lit mieux avec un inject :

 ar.inject({}) { |memo, subhash| subhash.each { |prod, value| memo[prod] ||= 0 ; memo[prod] += value } ; memo } => {"Dry Goods"=>5, "Vegetable"=>15} 

Mieux encore, si vous utilisez Hash.new avec une valeur par défaut de 0:

 ar.inject(Hash.new(0)) { |memo, subhash| subhash.each { |prod, value| memo[prod] += value } ; memo } => {"Dry Goods"=>5, "Vegetable"=>15} 

Ou si l’ inject fait mal à la tête:

 result = Hash.new(0) ar.each { |subhash| subhash.each { |prod, value| result[prod] += value } } result => {"Dry Goods"=>5, "Vegetable"=>15} 

Je ne suis pas sûr qu’un hash soit ce que vous voulez, car je ne sais pas plusieurs entrées dans chaque hash. Je vais donc commencer par modifier un peu votre représentation des données.

 ProductCount=Struct.new(:name,:count) data = [ProductCount.new("Vegetable",10), ProductCount.new("Vegetable",5), ProductCount.new("Dry Goods",3), ProductCount.new("Dry Goods",2)] 

Si les hachages peuvent avoir plusieurs paires clé-valeur, alors ce que vous voulez probablement faire est

 data = [{"Vegetable"=>10}, {"Vegetable"=>5}, {"Dry Goods"=>3>}, {"Dry Goods"=>2}] data = data.map{|h| h.map{|k,v| ProductCount.new(k,v)}}.flatten 

Maintenant, utilisez la gemme de facettes comme suit

 require 'facets' data.group_by(&:name).update_values{|x| x.map(&:count).sum} 

Le résultat est

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

Si vous avez deux hachages avec plusieurs clés:

 h1 = { "Vegetable" => 10, "Dry Goods" => 2 } h2 = { "Dry Goods" => 3, "Vegetable" => 5 } details = {} (h1.keys | h2.keys).each do |key| details[key] = h1[key].to_i + h2[key].to_i end details