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

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

Rails在哈希数组中求和值

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在这个数组中出现多次相同的值,则总和为key。 例如。

  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 } 

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

任何帮助,想法或任何事情都将受到欢迎。

这看似有效

 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}] 

你可以使用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通过loading和avg值对元素进行分组,这将返回{grouped_values => [objects]}的哈希值
  • 来自上述分组的values [对象](因为我们没有特别关心密钥)
  • map到一个新的数组
  • inject会将第一个元素作为memo传递,并在第一次迭代时产生第二个元素。 在该备忘录将是来自注入的返回值之后,下一个元素将被产生到该块,直到没有剩余元素。
  • merge! 使用块语法允许您处理重复键。 在这种情况下,您想要添加值。

这使用Hash#update (aka merge! )的forms,它使用块来确定合并的两个哈希中存在的键的值。 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应用.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无法下定决心,所以他使用了两者。

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