[英]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.