3

I have an array of hashes:

[{:a => "63-aaa",:b =>1}, {:a => "90-bbb",:b => 89}, {:a => "63-aaa", :b => 1},{:a => "45-hhh", :b => 44}, {:a => "32-kkk", :b => 67}, {: => "90-bbb", :b => 89}]

a key has value 63-aaa, 90-bbb, 45-hhh and 32-kkk. I want to get the unique elements of the array on the basis of ids of values of a like:

[{:a => "63-aaa",:b =>1}, {:a => "90-bbb",:b => 89}, {:a => "45-hhh", :b => 44}, {:a => "32-kkk", :b => 67}]

2 Answers 2

8

You can pass a block to Array#uniq in this case:

arr.uniq { |hash| hash[:a] }
Sign up to request clarification or add additional context in comments.

1 Comment

Hey its awesome working like charm, thanks for your help @Vu Minh Tan
2

You don't appear to care which one of each set of duplicates is kept. Array#uniq, with a block, is purpose-built for that. It will keep the one with the smallest index. If you want the one with the largest index, just apply Array#reverse before and after uniq.

If, however, you wanted to use other information in each hash to determine which one to keep, you could do that by using the form of Hash#update (a.k.a. merge!) that takes a block:

arr = [{:a=>"63-aaa", :b=> 1}, {:a=>"90-bbb", :b=>89}, {:a=>"63-aaa", :b=>22},
       {:a=>"45-hhh", :b=>44}, {:a=>"32-kkk", :b=>67}, {:a=>"90-bbb", :b=>14}]

arr.each_with_object({}) do |g,h|
  h.update({ g[:a]=>g }) { |k,oh,nh| <code to return oh or nh> }
end.values

Suppose, for example, you want to keep the hash h for which h[:b] is largest:

arr.each_with_object({}) do |g,h|
  h.update({ g[:a]=>g }) { |k,oh,nh| nh[:b] > oh[:b] ? nh : oh }
end.values
  #=> [{:a=>"63-aaa", :b=>22}, {:a=>"90-bbb", :b=>89},
  #    {:a=>"45-hhh", :b=>44}, {:a=>"32-kkk", :b=>67}]

I have created an empty hash (block variable h), and then for each hash g in arr, update h with the hash f = { g[:a]=>g }. If both h and f have the key g[:a], the block

{ |k,oh,nh| (nh[:b] > oh[:b]) ? nh : oh  }

is called upon to determine the value of the key g[:a] in h (that is, which of the two hashes to keep). The block variables are:

k  = g[:a]
oh = h[g[:a]]
nh = g

(Note that k is not used in the block, so we might write the block variables as |_,oh,nh|.)

each_with_object returns

h = {"63-aaa"=>{:a=>"63-aaa", :b=>22}, "90-bbb"=>{:a=>"90-bbb", :b=>89},
     "45-hhh"=>{:a=>"45-hhh", :b=>44}, "32-kkk"=>{:a=>"32-kkk", :b=>67}}

so we merely have to extract the values:

h.values
  #=> [{:a=>"63-aaa", :b=>22}, {:a=>"90-bbb", :b=>89},
  #    {:a=>"45-hhh", :b=>44}, {:a=>"32-kkk", :b=>67}]

Another way is to first execute:

arr.group_by { |h| h[:a] }.values
  #=> [[{:a=>"63-aaa", :b=> 1}, {:a=>"63-aaa", :b=>22}],
  #    [{:a=>"90-bbb", :b=>89}, {:a=>"90-bbb", :b=>14}],
  #    [{:a=>"45-hhh", :b=>44}],
  #    [{:a=>"32-kkk", :b=>67}]]

and then use whatever criteria you like to select one hash from each of the four arrays.

1 Comment

I have tried also this.Yes you are also right. Thanks for giving more knowledge on this. Thanks @Cary Swoveland

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.