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.