DEV Community

Burdette Lamar
Burdette Lamar

Posted on

Ruby Hash Default Values, Versatile (Of Course!)

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

You can set the the default value with method #default=:

h.default = false
h.default # => false
h[:nosuch] # => false
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

Note that in this case, the default value is ignored:

h.default = false
h[:nosuch] # => "Default value for nosuch"
Enter fullscreen mode Exit fullscreen mode

Note also that in the example above no entry for key :nosuch is created:

h.include?(:nosuch) # => false
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

That's all, Folks!

Top comments (0)