Ruby 对散列数组中的属性求和并将其分组
我有这样一个数组:Ruby 对散列数组中的属性求和并将其分组,ruby,Ruby,我有这样一个数组: 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=>
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\u 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
通过此操作和其他几次尝试,无法使其正常工作。以下代码正常工作。基本上有两个步骤: 按其
合作伙伴id
对哈希进行分组;用值
求和来加入这些组
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
的block 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
让我知道你是怎么处理这些的 试试这个
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\u values
很酷,但我们从ruby 2.4.0开始,否则使用map
,正如@SRack指出的那样这很简短,很好地强调了问题的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
您的问题涉及Ruby应用程序中的一个非常常见的操作。对于这类问题,通常可以采取两种方法。首先是采用这种方法 使用
分组依据
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[787]
#=> [{:partner_name=>"company 1", :partner_id=>787, :value=>1},
# {:partner_name=>"company 1", :partner_id=>787, :value=>2}]
是
当我们将散列的每个键值对转换为其他内容(散列)时,应该想到该方法。这里有一种方法可以实现这种转换
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})
常用的快捷方式
其他答案用较少的陈述说明了实现这一点的方法,但基本思想是相同的。顺便提一下,它在Ruby v2.4中首次亮相。要支持旧版本,请使用(akainject
)
使用(akamerge!
)的形式,使用一个块来确定合并的两个散列中存在的键的值。
通过这种方法,我们将构造一个散列散列,其键是:partners\u id
的不同值,但与group\u by
不同,其值是反映散列的给定键的键:value
总数的所需散列。完成后,我们只需返回hashhash
的值
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.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}]
让我们看看散列是如何构造的
我们首先计算一个枚举数
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({})>
导致生成枚举数的第一个值并将其传递给块,并使用消歧(有时称为解构)将值分配给块变量
看。我们现在可以执行块计算
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\u name=>“company 1”,“partner\u id=>787,:value=>1}}
合并到它中确实需要查询update
的块,因为合并的两个散列中都没有键代码>。将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}}
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}}
注意h
在每个步骤中是如何更新的。当生成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}}
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.merge(:value=>o[:value]+n[:value])
#=> h[787].merge( { :value=>1+2 }
#=> {:partner_name=>"company 1", :partner_id=>787, :valu}
返回
h的当前值,结束
hash`.Nice的构造。不知道转换\u值
+1New stuff bro:d考虑到提问者可能对Ruby很陌生,你认为你已经为你的答案提供了充分的解释吗?我相信你不会挖掘我的小编辑。通过将变量(res
)分配给哈希数组,读者可以在注释和答案中引用该变量,而无需定义它。对于SO问题中给出的所有示例中的所有输入对象,都应该这样做。这对@Mongoid有帮助吗?
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}