Reputation: 188
I have two hashes:
h1 = { "a" => 100, "b" => 200 }
h2 = { "b" => 254, "c" => 300 }
I need to return hash such as:
h3 = {
"a" => { "h1" => 100, "h2" => nil},
"b" => { "h1" => 200, "h2" => 254},
"c" => { "h1" => nil, "h2" => 300}
}
I am trying to use:
h1.merge(h2) { |k, val, old| { 'h1' => val, 'h2' => old } }
but it return only:
{"a"=>100, "b"=>{"h1"=>200, "h2"=>254}, "c"=>300}
without nil. Any ideas?
Upvotes: 2
Views: 68
Reputation: 110665
Here is another way that is somewhat general in terms of the numbers of hashes and hash labels:
h1 = { "a" => 100, "b" => 200 }
h2 = { "b" => 254, "c" => 300 }
h3 = { "c" => 111, "b" => 222 }
a = [h1,h2,h3]
labels = {h1=>"h1", h2=>"h2", h3=>"h3"}
h.reduce([]) { |keys,h| keys | h.keys }
.each_with_object({}) { |k,h|
h[k] = a.each_with_object({}) { |f,g| g[labels[f]] = f[k] } }
#=> {"a"=>{"h1"=>100, "h2"=>nil, "h3"=>nil},
# "b"=>{"h1"=>200, "h2"=>254, "h3"=>222},
# "c"=>{"h1"=>nil, "h2"=>300, "h3"=>111}}
The steps:
keys = h.reduce([]) { |keys,h| keys | h.keys }
#=> ["a", "b", "c"]
enum = keys.each_with_object({})
#=> #<Enumerator: ["a", "b", "c"]:each_with_object({})>
k,h = enum.next
#=> ["a", {}]
h[k] = a.each_with_object({}) { |f,g| g[labels[f]] = f[k] }
#=> {"h1"=>100, "h2"=>nil, "h3"=>nil}
k,h = enum.next
#=> ["b", {"a"=>{"h1"=>100, "h2"=>nil, "h3"=>nil}}]
h[k] = a.each_with_object({}) { |f,g| g[labels[f]] = f[k] }
#=> {"h1"=>200, "h2"=>254, "h3"=>222}
k,h = enum.next
#=> ["c", {"a"=>{"h1"=>100, "h2"=>nil, "h3"=>nil},
# "b"=>{"h1"=>200, "h2"=>254, "h3"=>222}}]
h[k] = a.each_with_object({}) { |f,g| g[labels[f]] = f[k] }
#=> {"h1"=>nil, "h2"=>300, "h3"=>111}
h
#=> {"a"=>{"h1"=>100, "h2"=>nil, "h3"=>nil},
# "b"=>{"h1"=>200, "h2"=>254, "h3"=>222},
# "c"=>{"h1"=>nil, "h2"=>300, "h3"=>111}}
Upvotes: 1
Reputation: 114138
The block is only invoked for duplicate keys. You can either ensure that both hashes contain all keys:
h1 = { "a" => 100, "b" => 200, "c" => nil }
h2 = { "a" => nil, "b" => 254, "c" => 300 }
h1.merge(h2) { |k, val, old| { 'h1' => val, 'h2' => old } }
#=> {"a"=>{"h1"=>100, "h2"=>nil},
# "b"=>{"h1"=>200, "h2"=>254},
# "c"=>{"h1"=>nil, "h2"=>300}}
Or build a new hash yourself, e.g. using the keys from both hashes:
h1 = { "a" => 100, "b" => 200 }
h2 = { "b" => 254, "c" => 300 }
(h1.keys | h2.keys).map { |k| [k, { 'h1' => h1[k], 'h2' => h2[k] }] }.to_h
#=> {"a"=>{"h1"=>100, "h2"=>nil},
# "b"=>{"h1"=>200, "h2"=>254},
# "c"=>{"h1"=>nil, "h2"=>300}}
Upvotes: 3
Reputation: 16506
If you read the official document for merge
(here) it say:
the value for each duplicate key is determined by calling the block with the key, its value in hsh and its value in other_hash.
This means for keys "a"
and "c"
, the block is never called (as they are not duplicate keys), as a result you are missing nil
in your result hash.
You can try this instead:
h3 = {}
(h1.keys + h2.keys).uniq.each{|a| h3[a] = {"h1" => h1[a], "h2" => h2[a]}}
h3
# => {"a"=>{"h1"=>100, "h2"=>nil}, "b"=>{"h1"=>200, "h2"=>254}, "c"=>{"h1"=>nil, "h2"=>300}}
Upvotes: 2