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?
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?
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.
In this code you create hashes by chain, so that any link of chain would have same default_proc
So, default_proc
of h
and h['bar']
and so far will be the same - it will return new instance of Hash
with this default_proc