Mering an array of hashes

I have two arrays of hashes and I would like to merge them.

One array has the array of:
{:id, :name}

The other, an array of:
{:name, :other_attributes*}

I’ve been raking my brains for hours on this one. Is there anyone who
can suggest the best way to merge these two arrays so that I can get the
resulting array of:
{:id. :name, :other_attributes}

Thanks so much in advance.

On Wed, Jun 4, 2008 at 12:58 PM, Luke G. [email protected]
wrote:

resulting array of:
{:id. :name, :other_attributes}

Is this what you are after? Not sure if I understood your description
correctly (an example of input and expected output would help):

irb(main):007:0> require ‘generator’
irb(main):007:0> a = [{:id => 1}, {:id => 2}]
irb(main):007:0> b = [{:name => “a”}, {:name => “b”}]
irb(main):007:0> s = SyncEnumerator.new a,b
irb(main):007:0> s.map{|a,b| a.merge(b)}

=> [{:name=>“a”, :id=>1}, {:name=>“b”, :id=>2}]

Hope this helps,

Jesus.

Close, but not quite.

Here’s an example:
hash #1: [{:name=>“File1.rar”,
:id=>“a3d80425a51a3a4cb2d7a3201439ef6cc3af5874”}, {:name=>“File2.avi”,
:id=>“58a658047ccb7333eb812da02dca9d84c1db2b41”}, {:name=>“File3.avi”,
:id=>“944f6a7c50a4a5cff0c28cb0f9158b16580b0310”}]

hash #2 [{:name=>“File2.avi”, :other_attributes =>"(350 MiB) - 2.85%
downloading at 0.00 B/s (UL at 0.00 B/s), stalled [error
(tracker-warning) - Tracker hasn’t responded yet. Retrying…]"},
{:name=>“File1.rar”, :other_attributes=>" (26.6 MiB) - 100% seeding at
0.00 B/s [0.23]"}, {:name=>“File3.avi”, :other_attributes=>"(35.5 MiB) -
0.86% downloading at 0.00 B/s (UL at 0.00 B/s), stalled"}]

Note that the hashes I want to merge aren’t necessarily at the same
index.

Sorry B, late at night here :stuck_out_tongue:

array #1: [{:name=>“File1.rar”,
:id=>“a3d80425a51a3a4cb2d7a3201439ef6cc3af5874”}, {:name=>“File2.avi”,
:id=>“58a658047ccb7333eb812da02dca9d84c1db2b41”}, {:name=>“File3.avi”,
:id=>“944f6a7c50a4a5cff0c28cb0f9158b16580b0310”}]

array #2 [{:name=>“File2.avi”, :other_attributes =>“stuff”},
{:name=>“File1.rar”, :other_attributes=>“stuff”}, {:name=>“File3.avi”,
:other_attributes=>“stuff”}]

And I would like the resulting array to look like:
array #1: [{:other_attributes => “stuff”, :name=>“File1.rar”,
:id=>“a3d80425a51a3a4cb2d7a3201439ef6cc3af5874”}, {:name=>“File2.avi”,
:id=>“58a658047ccb7333eb812da02dca9d84c1db2b41”, :other_attributes =>
“stuff”}, {:name=>“File3.avi”,
:id=>“944f6a7c50a4a5cff0c28cb0f9158b16580b0310”, :other_attributes =>
“stuff”}]

But I just have no idea how to do it. I’m sure there’s an easy ruby way
to do it somehow…

resulting array of:
{:id. :name, :other_attributes}

I’m very confused by your notation above. Sorry to sound picky, but
would
you be able to make it more clear?

Cheers,
B

I would do it like this:

a1 = [{:name=>“File1.rar”,
:id=>“a3d80425a51a3a4cb2d7a3201439ef6cc3af5874”},
{:name=>“File2.avi”,
:id=>“58a658047ccb7333eb812da02dca9d84c1db2b41”},
{:name=>“File3.avi”,
:id=>“944f6a7c50a4a5cff0c28cb0f9158b16580b0310”}]
a2 = [{:name=>“File2.avi”, :other_attributes =>“stuff”},
{:name=>“File1.rar”, :other_attributes=>“stuff”},
{:name=>“File3.avi”, :other_attributes=>“stuff”}]

a3 = Array.new
a1.each do |entry1|
a2.each do |entry2|
if entry1[:name].eql? entry2[:name]
a3.push(entry1.merge(entry2))
break
end
end
end
p a3

Ah, that does work too!

A friend of mine told me what I “should” be doing to get it working, but
left the exercise of the coding it up to me.

Here’s the magic:
hash_b.each{|d| d.merge!(hash_a.select{|e| e[:name] == d[:name]}.first)}

And I would like the resulting array to look like:
array #1: [{:other_attributes => “stuff”, :name=>“File1.rar”,
:id=>“a3d80425a51a3a4cb2d7a3201439ef6cc3af5874”}, {:name=>“File2.avi”,
:id=>“58a658047ccb7333eb812da02dca9d84c1db2b41”, :other_attributes =>
“stuff”}, {:name=>“File3.avi”,
:id=>“944f6a7c50a4a5cff0c28cb0f9158b16580b0310”, :other_attributes =>
“stuff”}]

But I just have no idea how to do it. I’m sure there’s an easy ruby way
to do it somehow…

Ah, okay, that’s clear, but pretty weird data :slight_smile:

id_rows = a.map {|r| r.values_at(:name, :id)}
other_att_rows = b.map {|r| r.values_at(:name, :other_attributes)}
name_to_attr_map = Hash[*other_att_rows.flatten]
output = id_rows.map {|r| Hash[:name, r[0], :id, r[1],
:other_attributes,
name_to_attr_map[r[0]]]}

Will work, and can be readily improved (exercise for reader :wink: - it’ll
be
niceer without turning the data in to an array of rows as I do in first
two lines).

Actually, complete code:
a1 = [{:name=>“File1.rar”,
:id=>“a3d80425a51a3a4cb2d7a3201439ef6cc3af5874”}, {:name=>“File2.avi”,
:id=>“58a658047ccb7333eb812da02dca9d84c1db2b41”}, {:name=>“File3.avi”,
:id=>“944f6a7c50a4a5cff0c28cb0f9158b16580b0310”}]

a2 = [{:name=>“File2.avi”, :other_attributes =>"(350 MiB) - 2.85%
downloading at 0.00 B/s (UL at 0.00 B/s), stalled [error
(tracker-warning) - Tracker hasn’t responded yet. Retrying…]"},
{:name=>“File1.rar”, :other_attributes=>" (26.6 MiB) - 100% seeding at
0.00 B/s [0.23]"}, {:name=>“File3.avi”, :other_attributes=>"(35.5 MiB) -
0.86% downloading at 0.00 B/s (UL at 0.00 B/s), stalled"}]

a2.each{|d| d.merge!(a1.select{|e| e[:name] == d[:name]}.first)}

Luke G. wrote:

Ah, that does work too!

A friend of mine told me what I “should” be doing to get it working, but
left the exercise of the coding it up to me.

Here’s the magic:
hash_b.each{|d| d.merge!(hash_a.select{|e| e[:name] == d[:name]}.first)}

0.00 B/s [0.23]"}, {:name=>“File3.avi”, :other_attributes=>"(35.5 MiB) -
0.86% downloading at 0.00 B/s (UL at 0.00 B/s), stalled"}]

a2.each{|d| d.merge!(a1.select{|e| e[:name] == d[:name]}.first)}

Hey - that comes out really cleanly. Cool.

Hi –

On Wed, 4 Jun 2008, Luke G. wrote:

0.00 B/s [0.23]"}, {:name=>“File3.avi”, :other_attributes=>"(35.5 MiB) -

Here’s the magic:
hash_b.each{|d| d.merge!(hash_a.select{|e| e[:name] == d[:name]}.first)}

You can use #find rather than #select plus #first.

David

On Wed, Jun 4, 2008 at 1:18 PM, Luke G. [email protected]
wrote:

Close, but not quite.

You already got other solutions, but here is another one:

irb(main):001:0> a = [{:id => 1, :name => “a”}, {:id => 2, :name =>
“b”}]
irb(main):002:0> b = [{:other => “other_b_data”, :name => “b”},
{:other => “other_a_data”, :name => “a”}]
irb(main):003:0> h = Hash.new {|h,k| h[k] = {}}
irb(main):013:0> (a + b).each {|data| h[data[:name]].merge!(data)}
irb(main):015:0> h.values
=> [{:other=>“other_a_data”, :name=>“a”, :id=>1},
{:other=>“other_b_data”, :name=>“b”, :id=>2}]

I haven’t measured the time, but this might have better performance
than finding in the second array by
name every time, since here we are accessing a hash by that key.

Hope this helps,

Jesus.