Ruby Hash: creating a default value for non-existing elements

hash, ruby

Solution

Hashes have a thing called a `default_proc`, which is simply a proc that Ruby runs when you try to access a hash key that doesn't exist. This proc receives both the hash itself and the target key as parameters.

You can set a Hash's `default_proc` at any time. Passing a block parameter to `Hash.new` simply allows you to initialize a Hash and set its `default_proc` in one step:

h = Hash.new
h.default_proc = proc{ |hash, key| hash[key] = 'foo' }

# The above is equivalent to:

h = Hash.new{ |hash, key| hash[key] = 'foo' }

We can also access the default proc for a hash by calling `h.default_proc`. Knowing this, and knowing that the ampersand (`&`) allows a proc passed as a normal parameter to be treated as a block parameter, we can now explain how this code works:

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

The block passed to `Hash.new` will be called when we try to access a key that doesn't exist. This block will receive the hash itself as `h`, and the key we tried to access as `k`. We respond by setting `h[k]` (that is, the value of the key we're trying to access) to a new hash. Into the constructor of this new hash, we pass the "parent" hash's `default_proc`, using an ampersand to force it to be interpreted as a block parameter. This is the equivalent of doing the following, to an infinite depth:

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

The end result is that the key we tried to access was initialized to a new Hash, which itself will initialize any "not found" keys to a new Hash, which itself will have the same behavior, etc. It's hashes all the way down.

Problem

I learned from this answer here that this is possible: ``` h = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) } h['bar'] # => {} h['tar']['star']['par'] # => {} ``` Can someone explain how it works?

Original source

Related problems