'Merge multiple array of hashes where a value matches in Ruby
I have 3 arrays with multiple hashes that I am trying to convert into a single array of hashes where a key/value matches
a = [{"name"=>"aaa", "a"=>"1", "b"=>"2"}, {"name"=>"bbb", "a"=>"1", "b"=>"2"}]
b = [{"name"=>"aaa", "c"=>"1", "d"=>"2"}, {"name"=>"bbb", "c"=>"1", "d"=>"2"}]
c = [{"name"=>"aaa", "e"=>"1", "f"=>"2"}, {"name"=>"bbb", "e"=>"1", "f"=>"2"}]
to get
combined = [{"name"=>"aaa", "a"=>"1", "b"=>"2", "c"=>"1", "d"=>"2", "e"=>"1", "f"=>"2"}, {"name"=>"bbb", "a"=>"1", "b"=>"2", "c"=>"1", "d"=>"2", "e"=>"1", "f"=>"2"}]
Solution 1:[1]
I think this is what you need.
hashes = [a, b, c]
result = hashes.inject([]) do |result, hash_array|
hash_array.each do |hash|
index = result.find_index{|item| item["name"] == hash["name"]}
if index
result[index] = result[index].merge(hash)
else
result << hash
end
end
result
end
Solution 2:[2]
This just looks cooler, and should still solve your case too:
a.zip(b,c).map{|vertical_array| e.inject({}) {|result, k| result.merge(k)} }
Console:
2.5.1 :016 > a.zip(b,c).map{|e|e.inject({}){|result, k| result.merge(k)} }
=> [{"name"=>"aaa", "a"=>"1", "b"=>"2", "c"=>"1", "d"=>"2", "e"=>"1", "f"=>"2"}, {"name"=>"bbb", "a"=>"1", "b"=>"2", "c"=>"1", "d"=>"2", "e"=>"1", "f"=>"2"}]
Solution 3:[3]
[a,b,c].transpose
.map { |arr| arr.reduce(&:merge) }
#=> [{"name"=>"aaa", "a"=>"1", "b"=>"2", "c"=>"1", "d"=>"2", "e"=>"1", "f"=>"2"},
{"name"=>"bbb", "a"=>"1", "b"=>"2", "c"=>"1", "d"=>"2", "e"=>"1", "f"=>"2"}]
If the ordering of the elements of a, b and c is not guaranteed, one may write
[a,b,c].map { |arr| arr.sort_by { |h| h["name"] } }
.transpose
.map { |arr| arr.reduce(&:merge) }
or
(a+b+c).each_with_object({}) do |g,h|
h.update(g["name"]=>g) { |_,o,n| o.merge(n) }
end.values
The latter uses the form of Hash#update (aka merge!) that employs a block ({ |_,o,n| o.merge(n) }) to determine the values of keys that are present in both hashes being merged. Note that here the receiver of Hash#values is
(a+b+c).each_with_object({}) do |g,h|
h.update(g["name"]=>g) { |_,o,n| o.merge(n) }
end
#=> {"aaa"=>{"name"=>"aaa", "a"=>"1", "b"=>"2", "c"=>"1",
# "d"=>"2", "e"=>"1", "f"=>"2"},
# "bbb"=>{"name"=>"bbb", "a"=>"1", "b"=>"2", "c"=>"1",
# "d"=>"2", "e"=>"1", "f"=>"2"}]
This calculation also permits greater variety in the content of the data. Suppose, for example:
a = [{"name"=>"aaa", "a"=>"1", "b"=>"2"}, {"name"=>"bbb", "a"=>"1", "b"=>"2"}]
b = [{"name"=>"bbb", "c"=>"1", "d"=>"2"}, {"name"=>"ccc", "c"=>"1", "d"=>"2"}]
c = [{"name"=>"ccc", "e"=>"1", "f"=>"2"}, {"name"=>"aaa", "e"=>"1", "f"=>"2"}]
Then
(a+b+c).each_with_object({}) do |g,h|
h.update(g["name"]=>g) { |_,o,n| o.merge(n) }
end
#=> {"aaa"=>{"name"=>"aaa", "a"=>"1", "b"=>"2", "e"=>"1", "f"=>"2"},
# "bbb"=>{"name"=>"bbb", "a"=>"1", "b"=>"2", "c"=>"1", "d"=>"2"},
# "ccc"=>{"name"=>"bbb", "c"=>"1", "d"=>"2", "e"=>"1", "f"=>"2"}]
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | javiyu |
| Solution 2 | Ricky Nguyen |
| Solution 3 |
