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