Ruby Weekly is a weekly newsletter covering the latest Ruby and Rails news.

How to Sort a Hash in Ruby

By Peter Cooper / February 14, 2011

Let's say you have the following hash of people to ages:

people = {
  :fred => 23,
  :joan => 18,
  :pete => 54
}

Now, what if we want to "sort" the hash into age order? We can't. At least, not exactly. Hashes are not meant to be in a certain order (though they are in Ruby 1.9) as they're a data structure where one thing merely relates to another thing. The relationships themselves are not elements of data in themselves.

We can, however, build other data structures that represent sorted versions of the data within a hash. An array, for example. Let's say we want to get a list of the ages in order:

people.values.sort    # => [18, 23, 54]

This gives us the values but you might want to have both the values and their associated keys in a particular order.

Enumerable To The Rescue!

Luckily, Hash has the Enumerable module mixed in which provides us with methods like sort and sort_by. Let's give them a go:

people.sort   # NoMethodError: undefined method `< =>' for :joan:Symbol

The sort method uses the <=> comparison operator to put things into order. Symbols don't have a <=> comparison operator (by default - you could create one!) so it's a fail. We can, however, use sort_by to get where we want to go:

people.sort_by { |name, age| age }
  # => [[:joan, 18], [:fred, 23], [:pete, 54]]

In this situation we're using sort_by to sort by a specific collection - the values (ages, in our case). Since integers (FixNum objects, in this case) can be compared with <=>, we're good to go. We get a nested array back with one element per hash element in order to preserve the 'ordering'. You can then use Array or other Enumerable methods to work with the result (tip: each is a good place to start!)

A More Complex Situation

Let's make our hash more complex:

people = {
  :fred => { :name => "Fred", :age => 23 },
  :joan => { :name => "Joan", :age => 18 },
  :pete => { :name => "Pete", :age => 54 }
}

This time we not only have a hash - we have a hash filled with other hashes. What if we want to sort by values within the nested hashes?

It's much the same as before, except we address the inner hash during the sort:

people.sort_by { |k, v| v[:age] }
  # => [[:joan, {:name=>"Joan", :age=>18}],
        [:fred, {:name=>"Fred", :age=>23}],
        [:pete, {:name=>"Pete", :age=>54}]]

In this way, we could even sort by the :name key, if we so chose.

Other Posts to Enjoy

Twitter Mentions