Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get the unique elements from an array of hashes in Ruby?

Tags:

I have an array of hashes, and I want the unique values out of it. Calling Array.uniq doesn't give me what I expect.

a = [{:a => 1},{:a => 2}, {:a => 1}] a.uniq # => [{:a => 1}, {:a => 2}, {:a => 1}] 

Where I expected:

[{:a => 1}, {:a => 2}] 

In searching around on the net, I didn't come up with a solution that I was happy with. Folks recommended redefining Hash.eql? and Hash.hash, since that is what Array.uniq is querying.

Edit: Where I ran into this in the real world, the hashes were slightly more complex. They were the result of parsed JSON that had multiple fields, some of which the values were hashes as well. I had an array of those results that I wanted to filter out the unique values.

I don't like the redefine Hash.eql? and Hash.hash solution, because I would either have to redefine Hash globally, or redefine it for each entry in my array. Changing the definition of Hash for each entry would be cumbersome, especially since there may be nested hashes inside of each entry.

Changing Hash globally has some potential, especially if it were done temporarily. I'd want to build another class or helper function that wrapped saving off the old definitions, and restoring them, but I think this adds more complexity than is really needed.

Using inject seems like a good alternative to redefining Hash.

like image 460
Aaron Hinni Avatar asked Oct 08 '08 01:10

Aaron Hinni


People also ask

What does .uniq do in Ruby?

uniq is a Ruby method that returns a new array by removing duplicate elements or values of the array.

What is array of hashes in Ruby?

Ruby's arrays and hashes are indexed collections. Both store collections of objects, accessible using a key. With arrays, the key is an integer, whereas hashes support any object as a key. Both arrays and hashes grow as needed to hold new elements.


1 Answers

I can get what I want by calling inject

a = [{:a => 1},{:a => 2}, {:a => 1}] a.inject([]) { |result,h| result << h unless result.include?(h); result } 

This will return:

[{:a=>1}, {:a=>2}] 
like image 194
Aaron Hinni Avatar answered Oct 11 '22 18:10

Aaron Hinni