vamsi
vamsi

Reputation: 93

Sorting both ascending and descending based on keys in array of hashes

I have the following array:

[
  {:string=>"2:aa/", :count=>2, :char=>"a"},
  {:string=>"2:dd/", :count=>2, :char=>"d"},
  {:string=>"2:ee/", :count=>2, :char=>"e"},
  {:string=>"=:gg/", :count=>2, :char=>"g"},
  {:string=>"1:ii/", :count=>2, :char=>"i"},
  {:string=>"=:nnn/", :count=>3, :char=>"n"},
  {:string=>"1:ooo/", :count=>3, :char=>"o"},
  {:string=>"2:sss/", :count=>3, :char=>"s"},
  {:string=>"1:uuu/", :count=>3, :char=>"u"}
]

I want this array of hashes to be sorted descending by count, and if count is equal, then I need to sort it ascending based on the char value.

Is there any direct way I can do this?

Upvotes: 0

Views: 665

Answers (3)

Mark Thomas
Mark Thomas

Reputation: 37507

You asked for a direct way, so here's a way to remove even another indirection--your intermediate data structure. This assumes your hash is just a means to an end and you intend to sort the strings:

strings = ["2:aa/", "2:dd/", "2:ee/", "=:gg/", "1:ii/", "=:nnn/", "1:ooo/", "2:sss/", "1:uuu/"]

strings.sort_by{ |s| x = s[/\w+/]; [-x.size, x] }

The regex isolates the part you need to count and sort; it may need to be adjusted if your real data differs from the example.

(I would replace x with a more meaningful variable name based on what the letters represent)

Upvotes: 1

Stefan
Stefan

Reputation: 114138

Instead of negating the value via -, you could also use sort with two arrays and switch the elements as needed.

To sort ascending / ascending you'd use: (see Array#<=>)

ary.sort { |a, b| [a[:count], a[:char]] <=> [b[:count], b[:char]] }

to sort descending / ascending, you switch the first elements:

ary.sort { |a, b| [b[:count], a[:char]] <=> [a[:count], b[:char]] }
#                  ^                         ^
#                  |                         |
#                  +-------------------------+

to sort ascending / descending, you switch the second elements: (you get the idea)

ary.sort { |a, b| [a[:count], b[:char]] <=> [b[:count], a[:char]] }
#                             ^                         ^
#                             |                         |
#                             +-------------------------+

Upvotes: 6

Ursus
Ursus

Reputation: 30056

Try this one

a.sort_by { |item| [-item[:count], item[:char]] }

a is your array

Upvotes: 5

Related Questions