Dennis
Dennis

Reputation: 1975

How to merge array of hashes based on hash value but not merge values instead override

I have an array of hashes like this:

[
  { name: 'Pratha', email: '[email protected]' },
  { name: 'John', email: '[email protected]' },
  { name: 'Clark', email: '[email protected]' },
]

And this is second group array of hashes:

[
  { name: 'AnotherNameSameEmail', email: '[email protected]' },
  { name: 'JohnAnotherName', email: '[email protected]' },
  { name: 'Mark', email: '[email protected]' },
]

What I want is, merge these two arrays into one, merge based on :email and keep latest (or first) :name.

Expected Result (latest name overrided):

[
  { name: 'AnotherNameSameEmail', email: '[email protected]' },
  { name: 'JohnAnotherName', email: '[email protected]' },
  { name: 'Mark', email: '[email protected]' },
  { name: 'Clark', email: '[email protected]' },
]

or (first name preserved)

[
  { name: 'Pratha', email: '[email protected]' },
  { name: 'John', email: '[email protected]' },
  { name: 'Mark', email: '[email protected]' },
  { name: 'Clark', email: '[email protected]' },
]

So, basically, I want to group by :email, retain one :name, drop dupe emails.

The examples found on SO is creates an array of values for :name.

Ruby 2.6.3

Upvotes: 1

Views: 121

Answers (2)

iGian
iGian

Reputation: 11193

Maybe you could just call Array#uniq with a block on email key of the concatenation (Array#+) of the two arrays:

(ary1 + ary2).uniq { |h| h[:email] }

Upvotes: 3

Cary Swoveland
Cary Swoveland

Reputation: 110725

a1 = [
  { name: 'Pratha', email: '[email protected]' },
  { name: 'John', email: '[email protected]' },
  { name: 'Clark', email: '[email protected]' },
]

a2 = [
  { name: 'AnotherNameSameEmail', email: '[email protected]' },
  { name: 'JohnAnotherName', email: '[email protected]' },
  { name: 'Mark', email: '[email protected]' },
]

Let's first keep the last:

(a1+a2).each_with_object({}) { |g,h| h.update(g[:email]=>g) }.values
  #=> [{:name=>"AnotherNameSameEmail", :email=>"[email protected]"},
  #    {:name=>"JohnAnotherName", :email=>"[email protected]"},
  #    {:name=>"Clark", :email=>"[email protected]"},
  #    {:name=>"Mark", :email=>"[email protected]"}]

To keep the first, do the same with (a1+a2) replaced with (a2+a1), to obtain:

  #=> [{:name=>"Pratha", :email=>"[email protected]"},
  #    {:name=>"John", :email=>"[email protected]"},
  #    {:name=>"Mark", :email=>"[email protected]"},
  #    {:name=>"Clark", :email=>"[email protected]"}]  

Upvotes: 2

Related Questions