The best kittens, technology, and video games blog in the world.

Thursday, July 13, 2006

Autovivification in Ruby

W00t, one of my favourite Perl features can be ported to Ruby in just a single line of code. As everyone knows, in Perl it's possible to write:

$x{a}{b}{c}{d} = "e";
And all intermediate-level hashtables will be created automagically. This is often extremely useful. On the other hand in lesser languages like Python or Java the only way to set a value in a nested hash is to create intermediate-level hashtables by hand:
x={}
x["a"] = {}
x["a"]["b"] = {}
x["a"]["b"]["c"] = {}
x["a"]["b"]["c"]["d"] = "e"
This may be a small thing, but multi-level hashes are used so often that one actually misses this functionality. For long I thought that Ruby doesn't have autovivification. And well, it doesn't have the full Perl thing, but it can get reasonably close with just this small definition:
def autovivifying_hash
   Hash.new {|ht,k| ht[k] = autovivifying_hash}
end
And now you can say:
x = autovivifying_hash
x["a"]["b"]["c"]["d"] = "e"
And that's going to be good enough most of the time :-)

4 comments:

Anonymous said...

Thank you, I was just looking for this. Perfect solution for me :)

robert said...

I find this solution more elegant:

h = Hash.new {|h,k| h[k] = Hash.new(&h.default_proc)}

Anonymous said...

In perl, you can also do
x{1}{'hello'}[4]{7}=5 ... I suppose doing this in ruby would be worse ...

taw said...

Anonymous: You pretty much never autovivify arrays like that by key assignment in practice, you only do that by pushing at the end of them.

Perl's syntax for push-autovivify is horrible.

Something like: push @{$x{1}{hello}}, {7 => 5}