我有一个这样的数组:
res = [
{:partner_name=>"company 1", :partner_id=>787, :value=>1},
{:partner_name=>"company 2", :partner_id=>768, :value=>1},
{:partner_name=>"company 3", :partner_id=>769, :value=>1},
{:partner_name=>"company 1", :partner_id=>787, :value=>2}
]
我尝试做的是创建一个数组,该数组将保存每partner_id
个值的总和。例如,上面的输出将是:
[{:partner_name=>"company 1", :partner_id=>787, :value=>3},
{:partner_name=>"company 2", :partner_id=>768, :value=>1},
{:partner_name=>"company 3", :partner_id=>769, :value=>1}]
试图玩弄它:
res.each do |r|
if hash.key?(r[:partner_id])
hash[:value] += r[:value]
else
hash = r
end
end
有了这个以及其他几次尝试,无法让它发挥作用。
答案 0 :(得分:2)
以下代码有效。基本上,有两个步骤:
按照partner_id
对哈希进行分组;使用value
求和来加入这些群组。
arr = [{ partner_name: "company 1", partner_id: 787, value: 1 },
{ partner_name: "company 2", partner_id: 768, value: 1},
{ partner_name: "company 3", partner_id: 769, value: 1},
{ partner_name: "company 1", partner_id: 787, value: 2}]
arr.group_by { |hash| hash[:partner_id] }.map do |_k, values|
{ partner_name: values.first[:partner_name],
partner_id: values.first[:partner_id],
value: values.sum { |val| val[:value] } }
end
或者还有下面这些内容,但它并没有很好地阅读,但却使用了merge
&#39}的块arg:
arr.group_by { |hash| hash[:partner_id] }.map do |_k, values|
values.reduce({}) do |a, e|
a.merge(e) do |key, old_val, new_val|
key == :value ? old_val += new_val : old_val
end
end
end
让我知道你如何继续这些!
答案 1 :(得分:1)
试试这个
arr.group_by { |item| item[:partner_id] }.transform_values do |items|
items_values_sum = items.sum { |item| item[:value] }
items.first.merge(value: items_values_sum)
end.values
transform_values
很酷但是我们来自ruby 2.4.0,否则使用map
作为@SRack指出
答案 2 :(得分:0)
这很简短,很好地强调了问题的Map / Reduce本质:
arr.group_by{|e| e[:partner_id]}.map do |_,v|
v.reduce{|r,e| r.merge! value: r[:value].to_i + e[:value] }
end
答案 3 :(得分:0)
您的问题涉及Ruby应用程序中非常常见的操作。对于这种问题,总有两种可用的方法。第一种是采用方法Enumerable#group_by。
使用group_by
hash = res.group_by { |h| h[:partner_id] }
#=> {787=>[{:partner_name=>"company 1", :partner_id=>787, :value=>1},
# {:partner_name=>"company 1", :partner_id=>787, :value=>2}],
# 768=>[{:partner_name=>"company 2", :partner_id=>768, :value=>1}],
# 769=>[{:partner_name=>"company 3", :partner_id=>769, :value=>1}]}
我们现在希望构造一个包含三个元素的数组,每个元素对应一个键值对hash
。该数组的第一个元素,由
hash[787]
#=> [{:partner_name=>"company 1", :partner_id=>787, :value=>1},
# {:partner_name=>"company 1", :partner_id=>787, :value=>2}]
是
{:partner_name=>"company 1", :partner_id=>787, :value=>3}
当我们将hash
的每个键值对转换为其他东西(哈希)时,应该想到方法Enumerable#map。这是进行转换的一种方法。
hash.map do |k,v|
# obtain the sum of the values of :value over each element (hash) of v
tot = v.sum { |h| h[:value] }
# merge { :value=>tot } into any element of `v` (say, v.first)
v.first.merge(:value=>tot)
end
#=> [{:partner_name=>"company 1", :partner_id=>787, :value=>3},
# {:partner_name=>"company 2", :partner_id=>768, :value=>1},
# {:partner_name=>"company 3", :partner_id=>769, :value=>1}]
v.first.merge(:value=>tot)
是v.first.merge( {:value=>tot} )
常用的快捷方式。
其他答案显示了用较少的陈述来做到这一点的方法,但基本思想是相同的。顺便说一句,Enumerable#sum在Ruby v2.4中首次亮相。要支持旧版本,请使用Enumerable#reduce(又名inject
)。
tot = v.reduce(0) { |t,h| t + h[:value] }
使用Hash#update(又名merge!
)的形式使用块来确定合并的两个哈希中存在的键的值。
使用这种方法,我们将构造一个哈希hash
,其键是:partners_id
的不同值,但与group_by
不同,其值是反映总数的所需哈希值:value
的给定键的键hash
。完成后,我们只返回哈希值hash
。
hash = res.each_with_object({}) do |g,h|
h.update(g[:partner_id]=>g) {|k,o,n| o.merge(:value=>o[:value]+n[:value])}
end
#=> {787=>{:partner_name=>"company 1", :partner_id=>787, :value=>3},
# 768=>{:partner_name=>"company 2", :partner_id=>768, :value=>1},
# 769=>{:partner_name=>"company 3", :partner_id=>769, :value=>1}}
数组hash
的值因此提供了所需的返回值。
hash.values
#=> [{:partner_name=>"company 1", :partner_id=>787, :value=>3},
# {:partner_name=>"company 2", :partner_id=>768, :value=>1},
# {:partner_name=>"company 3", :partner_id=>769, :value=>1}]
让我们看一下hash
是如何构建的。
我们首先计算一个枚举器。
enum = res.each_with_object({})
#=> #<Enumerator: [
# {:partner_name=>"company 1", :partner_id=>787, :value=>1},
# {:partner_name=>"company 2", :partner_id=>768, :value=>1},
# {:partner_name=>"company 3", :partner_id=>769, :value=>1},
# {:partner_name=>"company 1", :partner_id=>787, :value=>2}
# ]:each_with_object({})>
请注意
hash == enum.each do |g,h|
h.update(g[:partner_id]=>g) {|k,o,n| o.merge(:value=>o[:value]+n[:value])}
end
#=> true
Enumerator#each原因生成枚举数的第一个值并将其传递给块,并使用消歧将值分配给块变量(有时称为解构 EM>)。
g,h = enum.next
#=> [{:partner_name=>"company 1", :partner_id=>787, :value=>1}, {}]
g #=> {:partner_name=>"company 1", :partner_id=>787, :value=>1}
h #=> {}
见Enumerator#next。我们现在可以执行块计算。
h.update(g[:partner_id]=>g) {|k,o,n| o.merge(:value=>o[:value]+n[:value])}
#=> h.update(787=>{:partner_name=>"company 1", :partner_id=>787, :value=>1})
#=> {787=>{:partner_name=>"company 1", :partner_id=>787, :value=>1}}
由于h
为空(无密钥),因此将散列{ 787=>{:partner_name=>"company 1", :partner_id=>787, :value=>1} }
合并到其中需要查询update
块,因为没有密钥存在于两个哈希都是merged!
。将enum
的下两个值中的每一个传递给块后,情况也是如此。
g,h = enum.next
#=> [{:partner_name=>"company 2", :partner_id=>768, :value=>1},
# {787=>{:partner_name=>"company 1", :partner_id=>787, :value=>1}}]
g #=> {:partner_name=>"company 2", :partner_id=>768, :value=>1}
h #=> {787=>{:partner_name=>"company 1", :partner_id=>787, :value=>1}}
h.update(g[:partner_id]=>g)
#=> {787=>{:partner_name=>"company 1", :partner_id=>787, :value=>1},
# 768=>{:partner_name=>"company 2", :partner_id=>768, :value=>1}}
g,h = enum.next
#=> [{:partner_name=>"company 3", :partner_id=>769, :value=>1},
# {787=>{:partner_name=>"company 1", :partner_id=>787, :value=>1},
# 768=>{:partner_name=>"company 2", :partner_id=>768, :value=>1}}]
g #=> {:partner_name=>"company 3", :partner_id=>769, :value=>1}
h #=> {787=>{:partner_name=>"company 1", :partner_id=>787, :value=>1},
# 768=>{:partner_name=>"company 2", :partner_id=>768, :value=>1}}
h.update(g[:partner_id]=>g)
#=> {787=>{:partner_name=>"company 1", :partner_id=>787, :value=>1},
# 768=>{:partner_name=>"company 2", :partner_id=>768, :value=>1},
# 769=>{:partner_name=>"company 3", :partner_id=>769, :value=>1}}
注意每一步如何更新h
。当enum
的最终元素生成并传递给块时,事情会发生变化。
g,h = enum.next
#=> [{:partner_name=>"company 1", :partner_id=>787, :value=>2},
# {787=>{:partner_name=>"company 1", :partner_id=>787, :value=>1},
# 768=>{:partner_name=>"company 2", :partner_id=>768, :value=>1},
# 769=>{:partner_name=>"company 3", :partner_id=>769, :value=>1}}]
g #=> {:partner_name=>"company 1", :partner_id=>787, :value=>2}
h #=> {787=>{:partner_name=>"company 1", :partner_id=>787, :value=>1},
# 768=>{:partner_name=>"company 2", :partner_id=>768, :value=>1},
# 769=>{:partner_name=>"company 3", :partner_id=>769, :value=>1}}
h.update(g[:partner_id]=>g) {|k,o,n| o.merge(:value=>o[:value]+n[:value])}
#=> {787=>{:partner_name=>"company 1", :partner_id=>787, :value=>3},
# 768=>{:partner_name=>"company 2", :partner_id=>768, :value=>1},
# 769=>{:partner_name=>"company 3", :partner_id=>769, :value=>1}}
将{ g[:partner_id]=>g} #=> { 787=>g }
合并到h
后,我们发现两个哈希值都有公共密钥787
。因此,我们遵循该块来确定合并散列中787
的值。三个块变量的值如下。
k = 787 # the common key
o = h[787] # the "old" value of k
n = g # the "new" value of k
请注意
o[:value] #=> 1
n[:value] #=> 2
因此块计算
o.merge(:value=>o[:value]+n[:value])
#=> h[787].merge( { :value=>1+2 }
#=> {:partner_name=>"company 1", :partner_id=>787, :valu}
The current value of
{ħ{1}} hash`。