Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the Ruby equivalent of Python's defaultdict?

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)
like image 337
Ollie Glass Avatar asked Nov 03 '13 20:11

Ollie Glass


3 Answers

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 } 
like image 64
mu is too short Avatar answered Sep 21 '22 14:09

mu is too short


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 } 
like image 24
tessi Avatar answered Sep 22 '22 14:09

tessi


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'}
like image 31
Mark Thomas Avatar answered Sep 22 '22 14:09

Mark Thomas