Sergei Struk
Sergei Struk

Reputation: 358

Sort array of hashes having duplicate values

I have the following array of hashes:

arr = [{:id=>1, :val=>30}, {:id=>2, :val=>30}, {:id=>1002, :val=>82}]

I need to:

like this:

arr.sort_by_ascending_order.map{|x| x[:id]}
# => [1, 2, 1002] 
arr.sort_by_descending_order.map{|x| x[:id]}
# => [1002, 2, 1]

I cannot guarantee the second condition. For example,

arr.sort{|x,y| x[:val] <=> y[:val]}.map{|x| x[:id]}
# => [1, 2, 1002] 
arr.sort{|x,y| y[:val] <=> x[:val]}.map{|x| x[:id]}
# => [1002, 1, 2] 

Does anybody know how to fix it?

Upvotes: 0

Views: 677

Answers (1)

Stefan
Stefan

Reputation: 114178

You can provide an array:

arr = [{:id=>1, :val=>30}, {:id=>2, :val=>30}, {:id=>1002, :val=>82}]

arr.sort { |x, y| [x[:val], x[:id]] <=> [y[:val], y[:id]] }
#=> [{:id=>1, :val=>30}, {:id=>2, :val=>30}, {:id=>1002, :val=>82}]

arr.sort { |x, y| [y[:val], y[:id]] <=> [x[:val], x[:id]] }
#=> [{:id=>1002, :val=>82}, {:id=>2, :val=>30}, {:id=>1, :val=>30}]

This will sort by :val first and then by :id (see Array#<=> for details).

You could also use sort_by:

arr.sort_by { |h| [h[:val], h[:id]] }
#=> [{:id=>1, :val=>30}, {:id=>2, :val=>30}, {:id=>1002, :val=>82}]

Update:

Of course, this is not limited to hash values. You could use the element's index (as suggested by sawa) as a secondary sort condition to achieve a stable sort:

arr.sort_by.with_index { |h, i| [h[:val], i] }
#=> [{:id=>1, :val=>30}, {:id=>2, :val=>30}, {:id=>1002, :val=>82}]

Upvotes: 4

Related Questions