Playing with ruby hashes

Thu May 2014

Ruby's Hash#new method accepts an optional parameter which is returned when you try to access a key that does not exist in the hash (the default is nil). For example:

h = Hash.new('bar') #=> {}
puts h[:foo]        #=> 'bar'
h = Hash.new('bar') #=> {}
puts h[:foo]        #=> 'bar'

While the first example may not very useful, Hash#new also accepts a block that will be called with the hash object and the key whenever you try to access a key that does not exist in the hash, for example:

upcase = Hash.new { |hash, k| "#{k}".upcase }  #=> {}
upcase['foo']                                  #=> "FOO"
upcase = Hash.new { |hash, k| "#{k}".upcase }  #=> {}
upcase['foo']                                  #=> "FOO"

You can also modify the hash within the callback. Here's a more useful example where we create a memoized version of the fibonacci sequence:

fibonacci = Hash.new do |hash, k|
  case k
  when 0
    hash[k] = 0
  when 1, 2
    hash[k] = 1
  else
    hash[k] = hash[k-1] + hash[k-2]
  end
end
fibonacci = Hash.new do |hash, k|
  case k
  when 0
    hash[k] = 0
  when 1, 2
    hash[k] = 1
  else
    hash[k] = hash[k-1] + hash[k-2]
  end
end

Using this hash, whenever we compute the nth fibonacci number all the n - 1 fibonacci numbers will be cached in the hash, significantly reducing the number of recursive calls needed to compute the next fibonacci number.

puts fibonacci[5]    #=> 5
puts fibonacci       #{2=>1, 1=>1, 3=>2, 4=>3, 5=>5}
puts fibonacci[5]    #=> 5
puts fibonacci       #{2=>1, 1=>1, 3=>2, 4=>3, 5=>5}