Question

In Python, I can make a hash where each element has a default value when it's first referenced (also know as "autovivification"). Here's an example:

from collections import defaultdict
d = defaultdict(int)
d["new_key"] += 1
print d

Printing the dict shows the value for "new_key" is 1.

What's the equivalent in Ruby? This code throws an error:

d = {}
d[:new_key] += 1
puts d

test.rb:3:in `<main>': undefined method `+' for nil:NilClass (NoMethodError)
Was it helpful?

Solution 2

You can use the first argument of the Hash.new method for that:

d = Hash.new 0
d[:new_key] += 1
d[:new_key] #=> 1
d[:foo]     #=> 0

Be careful - you might accidentally change the default value:

h = Hash.new("Go Fish")
h[:unknown_key]         #=> "Go Fish"
h[:unknown_key].upcase! #=> "GO FISH"
h[:next_key]            #=> "GO FISH"

As "mu is too short" pointed out in his answer, you should better use a proc, as in:

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

OTHER TIPS

You can assign a default value using default=:

d.default = 0

Note that this won't really autovivify though, it just makes d[:new_key] return a zero without actually adding a :new_key key. default= can also cause problems if you intend to modify the default value; that means that d.default = [ ] is almost always a mistake as the array will not be copied when the default is accessed.

A better choice is usually default_proc=:

d.default_proc = proc { |h, k| h[k] = 0 }

This allows you to have distinct default values and it allows you to add the new key (or not depending on how the proc is structured).

You can also set these when creating the Hash:

d = Hash.new(0)
d = Hash.new { |h, k| h[k] = 0 }

The standard new method for Hash accepts a block. This block is called in the event of trying to access a key in the Hash which does not exist. The block is passed the Hash itself and the key that was requested (the two parameters) and should return the value that should be returned for the requested key.

This can be used to create an autovivified hash, among other things:

h = Hash.new{ |h,k| h[k] = 'default value'}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top