Hash Defaults and Nested OrderedHash in Rails

Posted by Peter Wagenet Thu, 01 Oct 2009 01:43:00 GMT

Thanks to ruby_switcher.sh I now have Ruby 1.9 running on my machine. Subsequently, I’ve just run into my first Ruby 1.9 induced bug: I relied upon 1.9’s Hash ordering. Fortunately, Rails’ ActiveSupport has it’s own undocumented OrderedHash implementation for Ruby 1.8. Unfortunately you can’t create an OrderdHash using the hash literal notation (i.e { :a => 1, :b => 2 }) so making a nested OrderedHash could be painful.

Consider the following code:

hash = ActiveSupport::OrderedHash.new
hash["2"] = ActiveSupport::OrderedHash.new
hash["2"]["b"] = "2.b"
hash["2"]["a"] = "2.a"
hash["1"] = ActiveSupport::OrderedHash.new
hash["1"]["b"] = "1.b"
hash["1"]["a"] = "1.a"

This however seems unnecessarily verbose. Thankfully Ruby’s Hash defaults come to the rescue.

hash = ActiveSupport::OrderedHash.new{|h,k| h[k] = ActiveSupport::OrderedHash.new(&h.default_proc) }
hash["2"]["b"] = "2.b"
hash["2"]["a"] = "2.a"
hash["1"]["b"] = "1.b"
hash["1"]["a"] = "1.a"

All the magic here happens in the somewhat arcane first line. Hash.new (and OrderedHash which is one of its subclasses) allows us to pass a block that defines the default behavior for undefined keys. The second bit of magic is the “h.default_proc” bit. This means that any automatically created OrderedHashes will have the same default behavior as their parent. As a result, we now have an OrderedHash that automatically creates OrderedHashes which in turn create OrderedHashes and so on.

The big benefit of this is that we don’t have to explicitly implement any of the child hashes. There is one gotcha however. You have to be careful with how you check for the existence of a key. While normally you could use “if hash[:key]” this will initialize an OrderedHash and will evaluate to true not to false. Instead, you should use “hash.has_key?(:key)”.

This is not a solution for all cases, but in some instances it can save you a few lines of clutter.

Comments

Leave a comment

Comments