Forum: Ruby How to merge two or more hashes in to one?

Posted by Jermaine O. (jermaine_o)
on 2012-11-06 14:06
Hi everyone.

I would like to merge two (or more) hashes into one single hash.
Imagine I have the following two hashes:

foo = {"luxuy" => ["Mercedes", "BMW"], "sport" => ["Ferrari"]}
bar = {"luxuy" => ["BMW", "Bentley"], "sport"=>["Lamborghini"]}

Then this is the result I'm looking for when merging the two:

{
 "luxuy" => ["Mercedes", "BMW", "Bentley"],
 "sport" => ["Ferrari","Lamborghini"]
}

Any thoughts of how to easily achieve this? I've messed around with
.merge but without any success.

Thanks!
Posted by Jan E. (jacques1)
on 2012-11-06 14:25
Hi,

this is a very special form of "merging", so I don't think there's a
built-in method for this.

But you can use the block form of Hash#merge:

#-------------------------------------
foo = {"luxuy" => ["Mercedes", "BMW"], "sport" => ["Ferrari"]}
bar = {"luxuy" => ["BMW", "Bentley"], "sport"=>["Lamborghini"]}

merg = foo.merge bar do |_, arr_1, arr_2|
  arr_1 | arr_2  # array union
end
p merg
#-------------------------------------
Posted by Joel Pearson (virtuoso)
on 2012-11-06 14:33
Off the top of my head I'd just do this:

foo.each do |one,two|
foo[one] << bar[one]
foo[one].flatten!
foo[one].uniq!
end
Posted by Jermaine O. (jermaine_o)
on 2012-11-06 14:59
Jan E. wrote in post #1083135:
> Hi,
>
> this is a very special form of "merging", so I don't think there's a
> built-in method for this.
>
> But you can use the block form of Hash#merge:
>
> #-------------------------------------
> foo = {"luxuy" => ["Mercedes", "BMW"], "sport" => ["Ferrari"]}
> bar = {"luxuy" => ["BMW", "Bentley"], "sport"=>["Lamborghini"]}
>
> merg = foo.merge bar do |_, arr_1, arr_2|
>   arr_1 | arr_2  # array union
> end
> p merg
> #-------------------------------------

Thanks, that worked out, but I'm wondering how this applies when I have 
more than 2 hashes. Because then this wouldn't work.
Posted by Jan E. (jacques1)
on 2012-11-06 15:09
Jermaine O. wrote in post #1083143:
> Thanks, that worked out, but I'm wondering how this applies when I have
> more than 2 hashes. Because then this wouldn't work.

So what? Simply merge the hashes one by one (in a loop, with 
Enumerable#inject or whatever).
Posted by Jermaine O. (jermaine_o)
on 2012-11-06 16:55
Jan E. wrote in post #1083144:
> Jermaine O. wrote in post #1083143:
>> Thanks, that worked out, but I'm wondering how this applies when I have
>> more than 2 hashes. Because then this wouldn't work.
>
> So what? Simply merge the hashes one by one (in a loop, with
> Enumerable#inject or whatever).

Hi Jan, I appreciate your help but care to give an example?
Posted by Jan E. (jacques1)
on 2012-11-06 17:14
Jermaine O. wrote in post #1083159:
> Hi Jan, I appreciate your help but care to give an example?

I suppose you have an array of hashes. Define a variable for the 
intermediate results and initialize it with an empty hash. Then you loop 
through the hashes and merge each one with the intermediate hash:

#-------------------------
data = [
  {luxury: ["Mercedes", "BMW"], sport: ["Ferrari"]},
  {luxury: ["BMW", "Bentley"], sport: ["Lamborghini"]},
  {luxury: ["something luxury"], sport: ["something sporty"]},
]

merged = {}
data.each do |cars|
  merged.merge!(cars) {|_, v_1, v_2| v_1 | v_2}
end
p merged
#-------------------------

The same thing can be achieved with Enumerable#inject, which is the 
generalization of this kind of "sum up the elements of a collection":

#-------------------------
merged = data.inject do |cars_1, cars_2|
  cars_1.merge(cars_2) {|_, v_1, v_2| v_1 | v_2}
end
p merged
#-------------------------
Posted by Jermaine O. (jermaine_o)
on 2012-11-06 17:29
> The same thing can be achieved with Enumerable#inject, which is the
> generalization of this kind of "sum up the elements of a collection":

Just what I need. Many thanks for this, works great. Appreciate it!
Posted by Robert Klemme (robert_k78)
on 2012-11-06 23:38
(Received via mailing list)
On Tue, Nov 6, 2012 at 5:29 PM, Jermaine O. <lists@ruby-forum.com> 
wrote:

> > The same thing can be achieved with Enumerable#inject, which is the
> > generalization of this kind of "sum up the elements of a collection":
>
> Just what I need. Many thanks for this, works great. Appreciate it!
>

I'd rather use Set here since apparently entries must be present only 
once:

require 'set'

merged = Hash.new {|h,k| h[k] = Set.new}

hashes.each do |hash|
  hash.each {|k, v| merged[k].merge(v)}
end

Cheers

robert
Posted by Jermaine O. (jermaine_o)
on 2012-11-07 09:42
> I'd rather use Set here since apparently entries must be present only
> once:
>
> require 'set'

Interesting, didn't knew about 'set'. Thanks.
Posted by Jan E. (jacques1)
on 2012-11-07 10:26
Well, in this case I don't really see the advantage of using sets. The 
only difference it makes is that arr_1 | arr_2 becomes 
set_1.merge(set_2).

So unless you plan to actually use the special features of sets, I'd 
stick with standard arrays.

But I don't want this to turn into yet another endless discussion about 
implementation details, so do whatever you think fits best.
Posted by Jermaine O. (jermaine_o)
on 2012-11-07 10:34
Jan E. wrote in post #1083271:
> Well, in this case I don't really see the advantage of using sets. The
> only difference it makes is that arr_1 | arr_2 becomes
> set_1.merge(set_2).
>
> So unless you plan to actually use the special features of sets, I'd
> stick with standard arrays.
>
> But I don't want this to turn into yet another endless discussion about
> implementation details, so do whatever you think fits best.

Got the work done and stuck with the merge block approach.
Posted by Robert Klemme (robert_k78)
on 2012-11-07 15:04
(Received via mailing list)
On Wed, Nov 7, 2012 at 10:34 AM, Jermaine O. <lists@ruby-forum.com> 
wrote:

>
Yeah.  I just tend to use Set in situations where the content is 
supposed
to be unique - if not for efficiency reasons then for documentation.


> Got the work done and stuck with the merge block approach.


Sorry, what did you mean?  Are you still stuck or were you stuck?

Kind regards

robert
Posted by Jermaine O. (jermaine_o)
on 2012-11-07 15:08
> Sorry, what did you mean?  Are you still stuck or were you stuck?
>
> Kind regards
>
> robert

No :) I meant that I sticked with the approach of using the block form 
of Hash#merge
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.