Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I sort a Ruby 2D array by multiple strings descending?

I have an array in Ruby (1.9.3) in which each element describes several parameters of an airport:

@airport_array = Array.new    
@airports.each do |airport|
  @airport_array.push({:id => airport.id, :iata_code => airport.iata_code, :city => airport.city, :country => airport.country})
end

In particular, :city and :country are both strings, and I would like to be able to sort by country in reverse alphabetical order and then city in alphabetical order.

I have been able to sort integers using something like this:

@airport_array = @airport_array.sort_by {|airport| [-airport[:country], airport[:city]]}

However, this syntax (in particular, using the - sign to denote a reverse sort) doesn't seem to work with strings. I get the following error:

undefined method `-@' for "United States":String

If I remove the minus sign, I don't get an error, but as expected, the sort is alphabetical for both parameters.

Is there a way I can sort this array by two strings, with exactly one string in reverse alphabetical order?

As an example, say I have the following array:

[{:id=>1, :iata_code=>"SEA", :city=>"Seattle", :country=>"United States"},
{:id=>2, :iata_code=>"DEN", :city=>"Denver", :country=>"United States"},
{:id=>3, :iata_code=>"YVR", :city=>"Vancouver", :country=>"Canada"},
{:id=>4, :iata_code=>"HNL", :city=>"Honolulu", :country=>"United States"},
{:id=>5, :iata_code=>"YOW", :city=>"Ottawa", :country=>"Canada"},
{:id=>6, :iata_code=>"YHZ", :city=>"Halifax", :country=>"Canada"}]

After I sort it, I would like to have the following array:

[{:id=>2, :iata_code=>"DEN", :city=>"Denver", :country=>"United States"},
{:id=>4, :iata_code=>"HNL", :city=>"Honolulu", :country=>"United States"},
{:id=>1, :iata_code=>"SEA", :city=>"Seattle", :country=>"United States"},
{:id=>6, :iata_code=>"YHZ", :city=>"Halifax", :country=>"Canada"},
{:id=>5, :iata_code=>"YOW", :city=>"Ottawa", :country=>"Canada"},
{:id=>3, :iata_code=>"YVR", :city=>"Vancouver", :country=>"Canada"}]

So the countries are in reverse alphabetical order (U, C), and then within a country, the cities are in alphabetical order (D, H, S and H, O, V).

like image 681
bogardpd Avatar asked Jul 22 '26 14:07

bogardpd


1 Answers

I thought I was going to get this to work using Enumerable#sort_by but I ran into the same issue you did so I used sort with a block. Enumerable#sort with a block is known to be slower than sort_by so I'm curious how others might answer this.

I got it working using:

arr.sort { |a, b| [b[:country], a[:city]] <=> [a[:country], b[:city]] }

It looks like this:

[76] pry(main)> arr
=> [{:id=>1, :iata_code=>"SEA", :city=>"Seattle", :country=>"United States"},
 {:id=>2, :iata_code=>"DEN", :city=>"Denver", :country=>"United States"},
 {:id=>3, :iata_code=>"YVR", :city=>"Vancouver", :country=>"Canada"},
 {:id=>4, :iata_code=>"HNL", :city=>"Honolulu", :country=>"United States"},
 {:id=>5, :iata_code=>"YOW", :city=>"Ottawa", :country=>"Canada"},
 {:id=>6, :iata_code=>"YHZ", :city=>"Halifax", :country=>"Canada"},
 {:id=>2, :iata_code=>"DOV", :city=>"Dover", :country=>"United States"}]

[77] pry(main)> arr.sort { |a, b| [b[:country], a[:city]] <=> [a[:country], b[:city]]  }
=> [{:id=>2, :iata_code=>"DEN", :city=>"Denver", :country=>"United States"},
 {:id=>2, :iata_code=>"DOV", :city=>"Dover", :country=>"United States"},
 {:id=>4, :iata_code=>"HNL", :city=>"Honolulu", :country=>"United States"},
 {:id=>1, :iata_code=>"SEA", :city=>"Seattle", :country=>"United States"},
 {:id=>6, :iata_code=>"YHZ", :city=>"Halifax", :country=>"Canada"},
 {:id=>5, :iata_code=>"YOW", :city=>"Ottawa", :country=>"Canada"},
 {:id=>3, :iata_code=>"YVR", :city=>"Vancouver", :country=>"Canada"}]
like image 123
Anthony Avatar answered Jul 24 '26 04:07

Anthony



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!