如果它们具有相同的值,则在散列数组中求和值

时间:2017-05-09 17:52:42

标签: arrays ruby hash

我在这篇文章中看到了这段代码,因为我试图根据一些标准对哈希数组中的值进行求和。

Rails sum values in an array of hashes

array = [
  {loading: 10, avg: 15, total: 25 },
  {loading: 20, avg: 20, total: 40 },
  {loading: 30, avg: 25, total: 55 }
]

sum = Hash.new(0)

array.each_with_object(sum) do |hash, sum|
  hash.each { |key, value| sum[key] += value }
end
 # => {:loading=>60, :avg=>60, :total=>120}

我正在尝试做什么而且我不知道怎样,如果在此数组中加载和avg在同一个值中出现多次相同的值,那么总和是否总和。例如。

 array = [
      {loading: 10, avg: 15, total: 25 },
      {loading: 20, avg: 20, total: 40 }, # See here
      {loading: 30, avg: 25, total: 55 },
      {loading: 20, avg: 20, total: 80 }, # See here
      {loading: 10, avg: 20, total: 46 }
    ]

结果将是:

[
  {loading: 10, avg: 15, total: 25 },
  {loading: 20, avg: 20, total: 120 }, # Results in this
  {loading: 30, avg: 25, total: 55 },
  {loading: 10, avg: 20, total: 46 }
]

我试图修改此行

hash.each { |key, value| sum[key] += value }

添加一个条件,检查值是否重复,但我没有成功。

欢迎任何帮助,想法或任何事情。

3 个答案:

答案 0 :(得分:4)

这似乎有效

array.group_by { |item| [item[:loading], item[:avg]] }.values.flat_map { |items| items.first.merge(total: items.sum { |h| h[:total] }) }
=> [{:loading=>10, :avg=>15, :total=>25}, {:loading=>20, :avg=>20, :total=>120}, {:loading=>30, :avg=>25, :total=>55}, {:loading=>10, :avg=>20, :total=>46}]

答案 1 :(得分:2)

您可以使用Enumerable#group_byHash#merge!来处理此问题

array.group_by {|h| [h[:loading],h[:avg]]}.values.map do |a|
  a.inject do |memo,h| 
    memo.merge!(h) do |_k,old_value,new_value| 
      old_value + new_value
    end
  end
end
#=> [{:loading=>10, :avg=>15, :total=>25}, 
#    {:loading=>40, :avg=>40, :total=>120}, 
#    {:loading=>30, :avg=>25, :total=>55}, 
#    {:loading=>10, :avg=>20, :total=>46}]

快速细分

  • array.group_by按加载和平均值对元素进行分组,这将返回{grouped_values =>的哈希值[对象]}
  • 来自上述分组的
  • values [个对象](因为我们并不特别关心密钥)
  • map成新数组
  • inject将第一个元素作为memo传递,并在第一次迭代时产生第二个元素。之后备忘录将是来自注入的返回值,并且下一个元素将被生成到块中,直到没有剩余元素为止。
  • 带有块语法的
  • merge!允许您处理重复的键。在这种情况下,您想要添加值。

答案 2 :(得分:1)

这使用Hash#update(aka merge!)的形式,它使用一个块来确定合并的两个哈希中存在的键的值。 1 请参阅用于块变量_kon定义的doc。第一个变量(_k)以下划线(通常只写_)开头,表示该块变量未在块计算中使用。 (注意@engineersmnky dd相同。)

arr = [
  {loading: 10, avg: 15, total: 25 },
  {loading: 20, avg: 20, total: 40 },
  {loading: 30, avg: 25, total: 55 },
  {loading: 20, avg: 20, total: 80 },
  {loading: 10, avg: 20, total: 46 }
]

arr.each_with_object({}) { |g,h| h.update(g.values_at(:loading, :avg)=>g) { |_k,o,n|
  o.merge(total: o[:total]+n[:total]) } }.values
  #=> [{:loading=>10, :avg=>15, :total=>25},
  #    {:loading=>20, :avg=>20, :total=>120},
  #    {:loading=>30, :avg=>25, :total=>55},
  #    {:loading=>10, :avg=>20, :total=>46}]

在最后应用.values之前,我们将构建以下哈希:

arr.each_with_object({}) { |g,h| h.update(g.values_at(:loading, :avg)=>g) { |_k,o,n|
  o.merge(total: o[:total]+n[:total]) } }
  #=> {[10, 15]=>{:loading=>10, :avg=>15, :total=>25},
  #    [20, 20]=>{:loading=>20, :avg=>20, :total=>120},
  #    [30, 25]=>{:loading=>30, :avg=>25, :total=>55},
  #    [10, 20]=>{:loading=>10, :avg=>20, :total=>46}}

如果@Ursus(他的回答先于我的)采用了这种方法,我会使用group_by。这两种方法似乎总是可以互换的。我不认为一个更好;这只是个人偏好的问题。似乎@engineersmnky无法下定决心,所以他使用了两者。

1。我觉得我已经写过这句话(逐字)数百次了。 ¯\ _(ツ)_ /¯