我正在尝试根据特定的键/值对合并散列数组。
array = [ {:id => '1', :value => '2'}, {:id => '1', :value => '5'} ]
我希望输出为
{:id => '1', :value => '7'}
正如patru所说,在sql术语中,这相当于:
SELECT SUM(value) FROM Hashes GROUP BY id
换句话说,我有一个包含记录的哈希数组。我想获得特定字段的总和,但总和将按键/值对分组。换句话说,如果我的选择标准是:id,如上例所示,那么它会将哈希值分成id为相同的组和其他键的总和。
我为之前的拼写错误而道歉。
答案 0 :(得分:1)
编辑:自从我第一次发布答案以来,问题已得到澄清。结果,我大幅修改了我的答案。
这是两个"标准"解决这个问题的方法。两者都使用Enumerable#select来首先从包含给定键/值对的数组(哈希)中提取元素。
<强>#1 强>
第一种方法使用Hash#merge!将每个数组元素(散列)顺序合并到最初为空的散列中。
<强>代码强>
def doit(arr, target_key, target_value)
qualified = arr.select {|h|h.key?(target_key) && h[target_key]==target_value}
return nil if qualified.empty?
qualified.each_with_object({}) {|h,g|
g.merge!(h) {|k,gv,hv| k == target_key ? gv : (gv.to_i + hv.to_i).to_s}}
end
示例强>
arr = [{:id => '1', :value => '2'}, {:id => '2', :value => '3'},
{:id => '1', :chips => '4'}, {:zd => '1', :value => '8'},
{:cat => '2', :value => '3'}, {:id => '1', :value => '5'}]
doit(arr, :id, '1')
#=> {:id=>"1", :value=>"7", :chips=>"4"}
<强>解释强>
此处的关键是使用Hash#merge!
的版本,该版本使用块来确定每个键/值对的值,其键出现在两个正在合并的哈希中。该键的两个值由块变量hv
和gv
表示。我们只想将它们加在一起。请注意,g
是each_with_object
创建的(最初为空)哈希对象,由doit
返回。
target_key = :id
target_value = '1'
qualified = arr.select {|h|h.key?(target_key) && h[target_key]==target_value}
#=> [{:id=>"1", :value=>"2"},{:id=>"1", :chips=>"4"},{:id=>"1", :value=>"5"}]
qualified.empty?
#=> false
qualified.each_with_object({}) {|h,g|
g.merge!(h) {|k,gv,hv| k == target_key ? gv : (gv.to_i + hv.to_i).to_s}}
#=> {:id=>"1", :value=>"7", :chips=>"4"}
<强>#2 强>
执行此类计算的另一种常用方法是使用Enumerable#flat_map,然后使用Enumerable#group_by。
<强>代码强>
def doit(arr, target_key, target_value)
qualified = arr.select {|h|h.key?(target_key) && h[target_key]==target_value}
return nil if qualified.empty?
qualified.flat_map(&:to_a)
.group_by(&:first)
.values.map { |a| a.first.first == target_key ? a.first :
[a.first.first, a.reduce(0) {|tot,s| tot + s.last}]}.to_h
end
<强>解释强>
这可能看起来很复杂,但是如果你把它分解成几步就不会那么糟糕。这里发生了什么。 (qualified
的计算与#1中的计算相同。)
target_key = :id
target_value = '1'
c = qualified.flat_map(&:to_a)
#=> [[:id,"1"],[:value,"2"],[:id,"1"],[:chips,"4"],[:id,"1"],[:value,"5"]]
d = c.group_by(&:first)
#=> {:id=>[[:id, "1"], [:id, "1"], [:id, "1"]],
# :value=>[[:value, "2"], [:value, "5"]],
# :chips=>[[:chips, "4"]]}
e = d.values
#=> [[[:id, "1"], [:id, "1"], [:id, "1"]],
# [[:value, "2"], [:value, "5"]],
# [[:chips, "4"]]]
f = e.map { |a| a.first.first == target_key ? a.first :
[a.first.first, a.reduce(0) {|tot,s| tot + s.last}] }
#=> [[:id, "1"], [:value, "7"], [:chips, "4"]]
f.to_h => {:id=>"1", :value=>"7", :chips=>"4"}
#=> {:id=>"1", :value=>"7", :chips=>"4"}
<强>注释强>
您可能希望考虑哈希整数中的值,并从target_key/target_value
中排除qualified
对:
arr = [{:id => 1, :value => 2}, {:id => 2, :value => 3},
{:id => 1, :chips => 4}, {:zd => 1, :value => 8},
{:cat => 2, :value => 3}, {:id => 1, :value => 5}]
target_key = :id
target_value = 1
qualified = arr.select { |h| h.key?(target_key) && h[target_key]==target_value}
.each { |h| h.delete(target_key) }
#=> [{:value=>2}, {:chips=>4}, {:value=>5}]
return nil if qualified.empty?
然后
qualified.each_with_object({}) {|h,g| g.merge!(h) { |k,gv,hv| gv + hv } }
#=> {:value=>7, :chips=>4}
或
qualified.flat_map(&:to_a)
.group_by(&:first)
.values
.map { |a| [a.first.first, a.reduce(0) {|tot,s| tot + s.last}] }.to_h
#=> {:value=>7, :chips=>4}