合并基于ruby中特定键/值对的哈希值

我试图基于特定的键/值对合并散列数组。

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为相同的组和其他键的总和。

对于由于之前的错字引起的任何混淆,我道歉。

编辑 :自从我第一次发布答案以来,问题已得到澄清。 结果,我大幅修改了我的答案。

以下是解决此问题的两种“标准”方法。 两者都使用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!的版本Hash#merge! 使用块来确定每个键/值对的值,其键出现在两个正在合并的哈希中。 该键的两个值由块变量hvgv 。 我们只想将它们加在一起。 请注意, g是由each_with_object创建的(最初为空)哈希对象,并由each_with_object返回。

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

评论

您可能希望考虑哈希整数中的值,并从qualified target_key/target_value排除target_key/target_value对:

 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}