[英]How to merge hashes with different key/value pairs in array of hashes? Ruby
Here is the array of hashes:这是哈希数组:
array = [
{:ID=>"aaa", :step2=>80},
{:ID=>"aaa", :step1=>160},
{:ID=>"aaa", :step3=>70},
{:ID=>"bbb", :step1=>80}
]
I'm trying to merge the hashes with the same :ID
and insert missing keys with value = 0, like follow:我正在尝试合并具有相同
:ID
的散列并插入 value = 0 的缺失键,如下所示:
array = [
{:ID=>"aaa", :step1 => 160, :step2 => 80, :step3 => 70},
{:ID=>"bbb", :step1 => 80, :step2 => 0, :step3 => 0}
]
Here is my solution:这是我的解决方案:
array = [
{ID: "aaa", step2: 80},
{ID: "aaa", step1: 160},
{ID: "aaa", step3: 70},
{ID: "bbb", step1: 80}
]
def group_by_id(hashes)
# gather all IDs
ids = hashes.map { |h| h[:ID] }.uniq
keys = hashes.reduce([]) { |keys, hash| keys |= hash.keys }
default_hash = {}
keys.each do |key|
default_hash[key] = 0
end
ids.map do |id|
hashes.select { |hash| hash[:ID] == id }
.reduce(default_hash) { |reduced, hash| reduced.merge(hash) }
end
end
desired_array = [
{ID: "aaa", step1: 160, step2: 80, step3: 70},
{ID: "bbb", step1: 80, step2: 0, step3: 0}
]
output = group_by_id(array)
puts output
puts desired_array == output
The #each_with_object
method may be useful here. #each_with_object
方法在这里可能很有用。 In this case we'll pass along a hash h
that gets updated for each element in array
.在这种情况下,我们将传递一个 hash
h
,它会针对array
中的每个元素进行更新。 That hash is then returned by the #each_with_object
method.然后由
#each_with_object
方法返回 hash。
Note: ||=
assigns the right hand side to the left hand side if the left hand side is nil
or false
.注意:
||=
如果左侧为nil
或false
,则将右侧分配给左侧。
array.each_with_object({}) { |x, h| (h[x[:ID]] ||= {}).update(x) }
Yields:产量:
{"aaa"=>{"ID"=>"aaa", "step3"=>70, "step1"=>160, "step2"=>80},
"bbb"=>{"ID"=>"bbb", "step1"=>80}}
Then we need only use #values
to get the data we want.然后我们只需要使用
#values
来获取我们想要的数据。
array
.each_with_object({}) { |x, h| (h[x[:ID]] ||= {}).update(x) }
.values
Yields:产量:
[{"ID"=>"aaa", "step3"=>70, "step1"=>160, "step2"=>80},
{"ID"=>"bbb", "step1"=>80}]
But you want missing keys filled in with 0
.但是您希望缺少用
0
填充的键。 For this we have to know what all of the keys are, and then we can use #each_with_object
again.为此,我们必须知道所有的键是什么,然后我们可以再次使用
#each_with_object
。
grouped = array
.each_with_object({}) { |x, h| (h[x[:ID]] ||= {}).update(x) }
.values
all_keys = grouped.map(&:keys).flatten.uniq
grouped.map! { |h| all_keys.each_with_object(h) { |k, _h| _h[k] ||= 0 } }
Now grouped
is:现在
grouped
为:
[{"ID"=>"aaa", "step2"=>80, "step1"=>160, "step3"=>70},
{"ID"=>"bbb", "step1"=>80, "step2"=>0, "step3"=>0}]
This can be done in four steps.这可以分四个步骤完成。
array = [{:ID=>"aaa", :step2=>80}, {:ID=>"aaa", :step1=>160},
{:ID=>"aaa", :step3=>70}, {:ID=>"bbb", :step1=>80}]
Construct a hash whose values are hashes that comprise the desired array to be returned, before missing zero-valued keys are added在添加缺少的零值键之前,构造一个 hash,其值是包含要返回的所需数组的哈希值
h = array.each_with_object({}) do |g,h|
h.update(g[:ID]=>g) { |_,o,n| o.merge(n)}
end
#=> {"aaa"=>{:ID=>"aaa", :step2=>80, :step1=>160, :step3=>70},
# "bbb"=>{:ID=>"bbb", :step1=>80, :step4=>40}}
See the form of Hash#update (aka merge
) that takes a block which returns the values of keys that are present in both hashes being merged.请参阅Hash#update (又名
merge
)的形式,它采用一个块,该块返回正在合并的两个哈希中存在的键的值。 Here that block is:这里的块是:
{ |_,o,n| o.merge(n)}
The block variable _
holds the value of the common key.块变量
_
保存公共键的值。 The main reason for using an underscore for that variable is to signal to the reader that that key is not used in the block calculation.为该变量使用下划线的主要原因是向读者表明该键未在块计算中使用。 See the doc for definitions of the block variables
o
and n
.有关块变量
o
和n
的定义,请参见文档。
Construct an array of all unique stepX
keys that appear in all elements of array
构造出现在数组所有元素中的所有唯一
stepX
键的array
step_keys = array.flat_map { |g| g.keys }.uniq - [:ID]
#=> [:step2, :step1, :step3, :step4]
See Enumerable#flat_map .请参阅Enumerable#flat_map 。
Add the missing keys添加缺少的键
step_keys.each_with_object(h) { |k,g| g.each_value { |v| v[k] ||= 0 } }
#=> {"aaa"=>{:ID=>"aaa", :step2=>80, :step1=>160, :step3=>70, :step4=>0},
# "bbb"=>{:ID=>"bbb", :step1=>80, :step4=>40, :step2=>0, :step3=>0}}
Now现在
h #=> {"aaa"=>{:ID=>"aaa", :step2=>80, :step1=>160, :step3=>70, :step4=>0},
# "bbb"=>{:ID=>"bbb", :step1=>80, :step4=>40, :step2=>0, :step3=>0}}
Return an array containing the values of h
返回一个包含
h
值的数组
h.values
#=> [{:ID=>"aaa", :step2=>80, :step1=>160, :step3=>70, :step4=>0},
# {:ID=>"bbb", :step1=>80, :step4=>40, :step2=>0, :step3=>0}]
These four statements could be combined into a single statement but I would not recommend doing that as readability would suffer and the code would be much harder to test.这四个语句可以组合成一个语句,但我不建议这样做,因为可读性会受到影响并且代码会更难测试。
Depending on requirements, one may be able to write:根据要求,可以编写:
a = array.each_with_object({}) do |g,h|
h.update(g[:ID]=>Hash.new(0).merge(g)) { |_,o,n| o.merge(n) }
end.values
#=> [{:ID=>"aaa", :step2=>80, :step1=>160, :step3=>70},
# {:ID=>"bbb", :step1=>80, :step4=>40}]
This returns the same array as before, but now:这将返回与以前相同的数组,但现在:
a[0][:step4]
#=> 0
even though the hash a[0]
has no key :step4
.即使 hash
a[0]
没有键:step4
。
See the form of Hash::new that takes an argument but now block, the argument being the default value .请参阅Hash::new的形式,它接受一个参数但现在阻塞,参数是默认值。 When a hash is defined
当定义了 hash
h = Hash.new(0)
then (possibly after keys have been added to h
), h[k]
returns the default value when h
does not have a key k
.然后(可能在将键添加到
h
之后),当h
没有键k
时, h[k]
返回默认值。
There are obvious considerations to weigh in determining if this variant would meet requirements.在确定此变体是否满足要求时,有明显的考虑因素需要权衡。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.