web-dev-qa-db-fra.com

Regroupez les hachages par clés et additionnez les valeurs

J'ai un tableau de hachages:

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

Je dois utiliser inject ici, je pense, mais j'ai vraiment du mal.

Je veux un nouveau hachage qui reflète la somme des clés en double du hachage précédent:

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

Je contrôle le code qui génère ce hachage afin que je puisse le modifier si nécessaire. Les résultats étaient principalement des hachages car cela pourrait finir par imbriquer n'importe quel nombre de niveaux en profondeur et il est ensuite facile d'appeler aplatir sur le tableau, mais pas d'aplatir les clés/valeurs du hachage aussi:

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 
40
blastula
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 initiale memo traite le premier élément du tableau comme memo, ce qui est bien ici.

84
steenslag

Utilisez simplement:

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

Tandis que le Hash.merge la technique 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 inject vous 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}
9
Head

Si vous disposez de 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
3
TuteC

Je ne suis pas sûr qu'un hachage soit ce que vous voulez ici, car je n'ai pas plusieurs entrées dans chaque hachage. je vais donc commencer par changer un peu la représentation de vos 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

Utilisez maintenant la gemme à 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}
3
Ken Bloom