【问题标题】:Group hashes by keys and sum the values按键对哈希进行分组并对值求和
【发布时间】:2011-05-26 02:35:14
【问题描述】:

我有一个哈希数组:

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

我想我需要在这里使用inject,但我真的很挣扎。

我想要一个新的哈希值,它反映了前一个哈希值的重复键的总和:

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

我控制着输出此哈希的代码,因此我可以在必要时对其进行修改。结果主要是散列,因为这最终可能会嵌套任意数量的深度,然后在数组上调用 flatten 很容易,但也不会展平散列的键/值:

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 

【问题讨论】:

标签: ruby hash key enumerable


【解决方案1】:
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 在找到重复时运行该块;没有初始memoinject 将数组的第一个元素视为memo,这在这里很好。

【讨论】:

  • 谢谢,这回答了问题,我不知道合并。非常感谢。
  • +1 这是 Ruby 书籍中必须包含的珠宝之一,但不是。
  • 能不能解释一下这里的命名,memo和el是什么意思?
  • @appleLover memoel 没有任何意义,你可以用任何词来交换它们。它们是变量名;我为memo =>I remember, el => element选择了这些@
  • 你能帮我理解这段代码吗,请提及 k、old_v、new_v 的值。我很困惑
【解决方案2】:

简单使用:

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

【讨论】:

  • 感谢您为未使用的变量使用下划线 _
【解决方案3】:
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}

更好的是,如果您使用默认值为 0 的 Hash.new

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}

【讨论】:

  • 名副其实的丰富建议
【解决方案4】:

我不确定哈希是否是您想要的,因为我在每个哈希中没有多个条目。所以我先稍微改变一下你的数据表示。

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

现在使用 facets gem 如下

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

结果是

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

【讨论】:

  • 谢谢!我知道但需要真正弄脏的结构,我完全不知道各个方面。我添加了输出哈希的原始代码,因为我可能可以在那里做一些更简单的事情。
【解决方案5】:

如果有两个带有多个键的哈希:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-03-03
    • 1970-01-01
    • 1970-01-01
    • 2011-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多