Here’s a quick and easy way to sort a Ruby hash by its keys. The ability to sort recursively and provide a custom sort block are also available features.

The Method

I took the approach of monkey patching a sort_by_key method into the Ruby Hash class itself. The method can be easily modified and placed into a utility class if you’d prefer to take more procedural approach.

class Hash
  def sort_by_key(recursive = false, &block)
    self.keys.sort(&block).reduce({}) do |seed, key|
      seed[key] = self[key]
      if recursive && seed[key].is_a?(Hash)
        seed[key] = seed[key].sort_by_key(true, &block)
      end
      seed
    end
  end
end

Basic Usage

Below is a very disorganized nested hash and an example of using the recursive sort option to get it back into line using Hash.sort_by_key(true).

h = {
  "b" => 2, "c" => 3, "a" => 1, "d" => {
    "b" => 2, "c" => 3, "a" => 1, "d" => {
      "b" => 2, "c" => 3, "a" => 1
    }
  }
}
h.sort_by_key(true) # =>
{
  "a" => 1, "b" => 2, "c" => 3, "d" => {
    "a" => 1, "b" => 2, "c" => 3, "d" => {
      "a" => 1, "b" => 2, "c" => 3
    }
  }
}

What About Mixed Type Hash Keys!?

If we continued the example from above by executing h[:a] = "one", and then calling h.sort_by_key, we would be slapped in the face with an exception of …

  • ArgumentError: comparison of String with :a failed

Looking at the backtrace it appears the call to self.keys.sort is the culprit.

So What’s The Solution?

There are a couple of solutions depending on your coding style and opinion on Ruby convention.

Option 1: Don’t do that!

Many coders in the Ruby community will argue that it’s not the job of Ruby to undo a user’s mistakes. While “Don’t do that!” that can sound like a copout, it’s also really good advice – keep things simple.

Generally, it’s not a good idea to mix the types of Hash keys. If you find that you’re getting this exception it’s probably an indication that “You’re doing it the wrong way.”

Option 2: Passing a custom sort block, and casting key values…

Coming from PHP I’ve grown accustomed to certain… luxuries, in that if you ask PHP to ksort() an array it will do its very best to get you something reasonable back. By passing a custom sort block to Hash.sort_by_keys we can mimic PHP’s robustness.

Let’s change how Hash.sort_by_keys is called by passing it a custom sort block that casts each key to a string before the comparison.

h.sort_by_key(true) {|x, y| x.to_s <=> y.to_s} # =>
{
  :a => "one", "a" => 1, "b" => 2, "c" => 3, "d" => {
    "a" => 1, "b" => 2, "c" => 3, "d" => {
      "a" => 1, "b" => 2, "c" => 3
    }
  }
}

It’s a very simplistic technique that should be capable of accommodating almost all of the off-the-beaten-path situations out there in the wild.

Summary

I ended up needing a variant of this solution to address a situation where http parameter ordering had to be alphabetical in order to create and validate signed requests to OAuth based APIs. So far things are working well and my code is a lot cleaner.

In my search for an existing sorting solution I also stumble upon this StackOverflow.com Question that shed some light on other hash key sorting techniques.

License

This code is released under the New-BSD License.

If you’ve found an issue, improvement, or bug with this technique please Contact Me, or comment below.

Add Eloquent ORM Tab to PHP Debug Bar

Using Eloquent outside of a Laravel project? – Me too!Collector Class class PHPDebugBarEloquentCollector extends \DebugBar\DataCollector\PDO\PDOCollector{ ... Continue reading

Doctrine 2 PDO Object

Published on August 17, 2015

WordPress Domain Changer 2.0

Published on November 19, 2014