[英]How to merge hashes with different key/value pairs in array of hashes? Ruby
这是哈希数组:
array = [
{:ID=>"aaa", :step2=>80},
{:ID=>"aaa", :step1=>160},
{:ID=>"aaa", :step3=>70},
{:ID=>"bbb", :step1=>80}
]
我正在尝试合并具有相同:ID
的散列并插入 value = 0 的缺失键,如下所示:
array = [
{:ID=>"aaa", :step1 => 160, :step2 => 80, :step3 => 70},
{:ID=>"bbb", :step1 => 80, :step2 => 0, :step3 => 0}
]
这是我的解决方案:
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
#each_with_object
方法在这里可能很有用。 在这种情况下,我们将传递一个 hash h
,它会针对array
中的每个元素进行更新。 然后由#each_with_object
方法返回 hash。
注意: ||=
如果左侧为nil
或false
,则将右侧分配给左侧。
array.each_with_object({}) { |x, h| (h[x[:ID]] ||= {}).update(x) }
产量:
{"aaa"=>{"ID"=>"aaa", "step3"=>70, "step1"=>160, "step2"=>80},
"bbb"=>{"ID"=>"bbb", "step1"=>80}}
然后我们只需要使用#values
来获取我们想要的数据。
array
.each_with_object({}) { |x, h| (h[x[:ID]] ||= {}).update(x) }
.values
产量:
[{"ID"=>"aaa", "step3"=>70, "step1"=>160, "step2"=>80},
{"ID"=>"bbb", "step1"=>80}]
但是您希望缺少用0
填充的键。 为此,我们必须知道所有的键是什么,然后我们可以再次使用#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 } }
现在grouped
为:
[{"ID"=>"aaa", "step2"=>80, "step1"=>160, "step3"=>70},
{"ID"=>"bbb", "step1"=>80, "step2"=>0, "step3"=>0}]
这可以分四个步骤完成。
array = [{:ID=>"aaa", :step2=>80}, {:ID=>"aaa", :step1=>160},
{:ID=>"aaa", :step3=>70}, {:ID=>"bbb", :step1=>80}]
在添加缺少的零值键之前,构造一个 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}}
请参阅Hash#update (又名merge
)的形式,它采用一个块,该块返回正在合并的两个哈希中存在的键的值。 这里的块是:
{ |_,o,n| o.merge(n)}
块变量_
保存公共键的值。 为该变量使用下划线的主要原因是向读者表明该键未在块计算中使用。 有关块变量o
和n
的定义,请参见文档。
构造出现在数组所有元素中的所有唯一stepX
键的array
step_keys = array.flat_map { |g| g.keys }.uniq - [:ID]
#=> [:step2, :step1, :step3, :step4]
请参阅Enumerable#flat_map 。
添加缺少的键
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}}
现在
h #=> {"aaa"=>{:ID=>"aaa", :step2=>80, :step1=>160, :step3=>70, :step4=>0},
# "bbb"=>{:ID=>"bbb", :step1=>80, :step4=>40, :step2=>0, :step3=>0}}
返回一个包含h
值的数组
h.values
#=> [{:ID=>"aaa", :step2=>80, :step1=>160, :step3=>70, :step4=>0},
# {:ID=>"bbb", :step1=>80, :step4=>40, :step2=>0, :step3=>0}]
这四个语句可以组合成一个语句,但我不建议这样做,因为可读性会受到影响并且代码会更难测试。
根据要求,可以编写:
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}]
这将返回与以前相同的数组,但现在:
a[0][:step4]
#=> 0
即使 hash a[0]
没有键:step4
。
请参阅Hash::new的形式,它接受一个参数但现在阻塞,参数是默认值。 当定义了 hash
h = Hash.new(0)
然后(可能在将键添加到h
之后),当h
没有键k
时, h[k]
返回默认值。
在确定此变体是否满足要求时,有明显的考虑因素需要权衡。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.