When you refer to a non-existent key, a Ruby Hash object returns its default value, right?
Well, not so fast!
The returned value for a nonexistent key is actually controlled by not one, but two, settings:
- Default value: a stored object.
- Default proc: a stored Proc.
Default Value
The simplest case is seen in a new Hash that was created with no argument:
- The default value is initially
nil
. - The default proc is initially
nil
.
h = Hash.new
h.default # => nil
h.default_proc # => nil
h[:nosuch] # => nil
Whenever the default proc is nil
, the returned default value for the Hash comes from #default
.
You can initialize the default value with method Hash.new
:
h = Hash.new(0)
h.default # => 0
h[:nosuch] # => 0
You can set the the default value with method #default=
:
h.default = false
h.default # => false
h[:nosuch] # => false
So far, so good. The default value has ruled. (BUT only because the default proc has been nil
.)
Default Proc
When the default proc is not nil
, the returned default value is determined by the default proc alone.
How? We'll get to that.
First, you can initialize the default proc by including a block with Hash.new
:
h = Hash.new { |hash, key| "Default value for #{key}" }
h.default_proc.class # => Proc
Or you can set the default proc with method #default_proc=
:
h = Hash.new
h.default_proc # => nil
h.default_proc = proc { |hash, key| "Default value for #{key}" }
h.default_proc.class # => Proc
When the default proc is set (i.e., not nil
), a reference to a non-existent key is handled thus:
- The proc is called with both the Hash object itself and the missing key.
- The block's return value is returned as the key's default value:
h = Hash.new { |hash, key| "Default value for #{key}" }
h[:nosuch] # => "Default value for nosuch"
Note that in this case, the default value is ignored:
h.default = false
h[:nosuch] # => "Default value for nosuch"
Note also that in the example above no entry for key :nosuch
is created:
h.include?(:nosuch) # => false
However, the block itself can add a new entry:
h = Hash.new { |hash, key| hash[key] = "Subsequent value for #{key}"; "First value for #{key}" }
h.include?(:nosuch) # => false
h[:nosuch] # => "First value for nosuch"
h.include?(:nosuch) # => true
h[:nosuch] # => "Subsequent value for nosuch"
h[:nosuch] # => "Subsequent value for nosuch"
Finally, setting the default proc back to nil
causes a missing-key reference to return the default value:
h.default_proc = nil
h.default = false
h[:nosuch] # => false
That's all, Folks!