基于红宝石中的特定键/值对合并哈希 [英] Merge hashes based on particular key/value pair in ruby

查看:128
本文介绍了基于红宝石中的特定键/值对合并哈希的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试基于特定的键/值对合并哈希数组.

I am trying to merge an array of hashes based on a particular key/value pair.

array = [ {:id => '1', :value => '2'}, {:id => '1', :value => '5'} ]

我希望输出为

{:id => '1', :value => '7'}

正如patru所说,用sql术语等同于:

As patru stated, in sql terms this would be equivalent to:

SELECT SUM(value) FROM Hashes GROUP BY id

换句话说,我有一个包含记录的哈希数组.我想获取特定字段的总和,但是总和将按键/值对分组.换句话说,如果如上例所示,我的选择标准是:id,则它将散列分为ID相同且其他键求和的散列.

In other words, I have an array of hashes that contains records. I would like to obtain the sum of a particular field, but the sum would grouped by key/value pairs. In other words, if my selection criteria is :id as in the example above, then it would seperate the hashes into groups where the id was the same and the sum the other keys.

对于先前由于错字引起的任何困惑,我深表歉意.

I apologize for any confusion due to the typo earlier.

推荐答案

编辑:自从我第一次发布答案以来,这个问题就已经澄清了.结果,我对答案做了实质性的修改.

Edit: The question has been clarified since I first posted my answer. As a result, I have revised my answer substantially.

以下是解决此问题的两种标准"方法.两者都首先使用 Enumerable#select 从包含给定键/值对的数组(哈希)中提取元素.

Here are two "standard" ways of addressing this problem. Both use Enumerable#select to first extract the elements from the array (hashes) that contain the given key/value pair.

#1

第一种方法使用 Hash#merge! 以将每个数组元素(哈希)顺序合并到最初为空的哈希中.

The first method uses Hash#merge! to sequentially merge each array element (hashes) into a hash that is initially empty.

代码

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!的版本,该版本使用一个块来确定每个键/值对的值,这些键/值对的键都出现在要合并的两个哈希中.该键的两个值在上面用块变量hvgv表示.我们只是想将它们添加在一起.请注意,g是由each_with_object创建并由doit返回的(最初为空)哈希对象.

The key here is to use the version of Hash#merge! that uses a block to determine the value for each key/value pair whose key appears in both of the hashes being merged. The two values for that key are represented above by the block variables hv and gv. We simply want to add them together. Note that g is the (initially empty) hash object created by each_with_object, and returned by 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 ,后跟可枚举#group_by .

The other common way to do this kind of calculation is to use Enumerable#flat_map, followed by 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中的计算相同.)

This may look complex, but it's not so bad if you break it down into steps. Here's what's happening. (The calculation of qualified is the same as in #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对:

You may wish to consider makin the values in the hashes integers and exclude the target_key/target_value pairs from 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}

这篇关于基于红宝石中的特定键/值对合并哈希的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆