'initializing' hashes in ruby - autovivification

Counting, sums, and nested hashes right off the bat

Posted on May 17, 2015

TL;DR: hash = Hash.new { |h, k| h[k] = 0 }

I’ve often needed to count or sum over a set of data. Daily sales, weekly user sign ups, and in my most recent project, monthly work units per employee. Here’s an example of summing number of orders per day:

hash = {}

orders.each do |o|
  hash[o.date] += 1
end
NoMethodError: undefined method `+' for nil:NilClass

This sucks because it’s such a frequently needed action. I don’t want to write in extraneous code to set the expected key values to 0. And I finally found a solution.

hash = Hash.new { |h, k| h[k] = 0 }

orders.each do |o|
  hash[o.date] += 1
end
{"2015-5-14"=>10, "2015-5-15"=>2, "2015-5-16"=>7}

This is called autovivification, which apparently comes from Perl. And it’s awesome. Any key’s value can have a default value set. Here’s another example that comes up a lot for me:

hash = {}

orders.each do |o|
  hash[o.date][:state] = o.state
end
NoMethodError: undefined method `[]' for nil:NilClass

Similarly, we can set the default value for keys to another hash, which lets us assign values for nested keys immediately.

hash = Hash.new { |h, k| h[k] = {} }

Advanced Ruby Hash Techniques

Here’s an awesome resource I found from Hacker News. It covers autovivification but has some other cool tricks too: blog.honeybadger.io/advanced-ruby-hash-techniques


Comments