Группируйте хэши по ключам и суммируйте значения - PullRequest
40 голосов
/ 15 декабря 2010

У меня есть массив хэшей:

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

Мне нужно использовать inject, я думаю, но я действительно боролся.

Я хочу новый хэш, который отражаетсумма дубликатов предыдущего хеш-кода:

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

Я управляю кодом, который выводит этот хэш, поэтому я могу изменить его при необходимости.Результатами были главным образом хэши, потому что это могло привести к вложению любого количества уровней вглубь, и тогда легко вызвать сглаживание в массиве, но не сглаживать также ключи / значения хеша:

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 

Ответы [ 5 ]

84 голосов
/ 15 декабря 2010
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 с блоком запускает блок, когда находит дубликат; inject без начального memo обрабатывает первый элемент массива как memo, что здесь хорошо.

11 голосов
/ 14 ноября 2013

Просто используйте:

array = [{"Vegetable"=>10}, {"Vegetable"=>5}, {"Dry Goods"=>3}, {"Dry Goods"=>2}]
array.inject{|a,b| a.merge(b){|_,x,y| x + y}}
8 голосов
/ 07 августа 2012
ar = [{"Vegetable"=>10}, {"Vegetable"=>5}, {"Dry Goods"=>3}, {"Dry Goods"=>2}]

Хотя метод Hash.merge работает нормально, я думаю, что он лучше читается с inject:

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

Еще лучше, если вы используете Hash.new со значением по умолчанию 0:

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

Или, если inject у вас болит голова:

result = Hash.new(0)
ar.each { |subhash| subhash.each { |prod, value| result[prod] += value } }
result
=> {"Dry Goods"=>5, "Vegetable"=>15}
3 голосов
/ 19 апреля 2012

Если есть два хэша с несколькими ключами:

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 голосов
/ 15 декабря 2010

Я не уверен, что вам нужен хеш, потому что я не делаю несколько записей в каждом хэше. поэтому я начну с небольшого изменения вашего представления данных.

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

Если хэши могут иметь несколько пар ключ-значение, то вы, вероятно, захотите сделать это

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

Теперь используйте драгоценный камень граней следующим образом

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

Результат

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