By default, attempting to lookup the value for a key which does not exist will return nil
. You can optionally specify some other value to return (or an action to take) when the hash is accessed with a non-existent key. Although this is referred to as "the default value", it need not be a single value; it could, for example, be a computed value such as the length of the key.
The default value of a hash can be passed to its constructor:
h = Hash.new(0)
h[:hi] = 1
puts h[:hi] # => 1
puts h[:bye] # => 0 returns default value instead of nil
A default can also be specified on an already constructed Hash:
my_hash = { human: 2, animal: 1 }
my_hash.default = 0
my_hash[:plant] # => 0
It is important to note that the default value is not copied each time a new key is accessed, which can lead to surprising results when the default value is a reference type:
# Use an empty array as the default value
authors = Hash.new([])
# Append a book title
authors[:homer] << 'The Odyssey'
# All new keys map to a reference to the same array:
authors[:plato] # => ['The Odyssey']
To circumvent this problem, the Hash constructor accepts a block which is executed each time a new key is accessed, and the returned value is used as the default:
authors = Hash.new { [] }
# Note that we're using += instead of <<, see below
authors[:homer] += ['The Odyssey']
authors[:plato] # => []
authors # => {:homer=>["The Odyssey"]}
Note that above we had to use += instead of << because the default value is not automatically assigned to the hash; using << would have added to the array, but authors[:homer] would have remained undefined:
authors[:homer] << 'The Odyssey' # ['The Odyssey']
authors[:homer] # => []
authors # => {}
In order to be able to assign default values on access, as well as to compute more sophisticated defaults, the default block is passed both the hash and the key:
authors = Hash.new { |hash, key| hash[key] = [] }
authors[:homer] << 'The Odyssey'
authors[:plato] # => []
authors # => {:homer=>["The Odyssey"], :plato=>[]}
You can also use a default block to take an action and/or return a value dependent on the key (or some other data):
chars = Hash.new { |hash,key| key.length }
chars[:test] # => 4
You can even create more complex hashes:
page_views = Hash.new { |hash, key| hash[key] = { count: 0, url: key } }
page_views["http://example.com"][:count] += 1
page_views # => {"http://example.com"=>{:count=>1, :url=>"http://example.com"}}
In order to set the default to a Proc on an already-existing hash, use default_proc=
:
authors = {}
authors.default_proc = proc { [] }
authors[:homer] += ['The Odyssey']
authors[:plato] # => []
authors # {:homer=>["The Odyssey"]}