Rails Goodies: ActiveSupport::OrderedHash
January 29, 2008 – 9:46 amRails is well-known for providing little things here and there that make developers’ lives easier. Sometimes they’re obvious, like the various inflection methods added to String (pluralize, titleize, etc.) - but just as often, these useful tidbits are hidden in the Rails internals. Just because they’re usually limited to the internals of the framework, though, doesn’t mean that these features can’t be useful to the everyday Rails coder, though, so I thought it’d be interesting to expose some of them.
The first of these Rails goodies I’m going to talk about is ActiveSupport::OrderedHash. This class is defined in activesupport/lib/active_support/ordered_options.rb, and is a subclass of Array. Here’s the definition (from edge):
module ActiveSupport
# Hash is ordered in Ruby 1.9!
if RUBY_VERSION >= '1.9'
OrderedHash = ::Hash
else
class OrderedHash < Array #:nodoc:
def []=(key, value)
if pair = assoc(key)
pair.pop
pair << value
else
self << [key, value]
end
end
def [](key)
pair = assoc(key)
pair ? pair.last : nil
end
def keys
collect { |key, value| key }
end
def values
collect { |key, value| value }
end
end
end
end
The first thing to note is that this class is not defined if you’re running Ruby 1.9 - these compatibility changes have been appearing recently in edge. Assuming you’re on 1.8.x, though, you will have the class defined. Basically, OrderedHash flattens a hash into an array of arrays. It doesn’t automatically sort the resulting structure, but it does preserve the order in which the elements were added, and you can manually sort it later:
>> hash = {:a => 1, :d => 4, :c => 3, :b => 2}
=> {:b=>2, :c=>3, :d=>4, :a=>1}
>> ohash = ActiveSupport::OrderedHash.new
=> []
>> hash.inject(ohash) do |all, row|
?> all[row.first] = row.last
>> all
>> end
=> [[:b, 2], [:c, 3], [:d, 4], [:a, 1]]
>> ohash.sort! {|x, y| x.first.to_s <=> y.first.to_s}
=> [[:a, 1], [:b, 2], [:c, 3], [:d, 4]]
In addition, OrderedHashes preserve the means of hash access, as well as the keys and values methods:
>> ohash[:a]
=> 1
>> ohash[:c]
=> 3
>> ohash.keys
=> [:a, :b, :c, :d]
>> ohash.values
=> [1, 2, 3, 4]
OrderedHash isn’t used much in the Rails internals - in fact, the only place it shows up is in the ActiveRecord::Calculations module, where it’s responsible for the output of calculations that include a :group parameter:
>> Line.count(:id, :group => :year)
=> [[nil, 25], [2004, 1], [2005, 3], [2006, 17], [2007, 7], [2008, 2]]
It can be useful in the code you write, however - so if you ever have the need to enforce some sorting on a hash, keep it in mind!
Note: Unless, of course, you’re running Ruby 1.9, in which case this is all handled for you automatically.

