Ruby Language Hashes Setting Default Values


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 =

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 =[])

# 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 = { [] }

# 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, 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,key| key.length }

chars[:test] # => 4

You can even create more complex hashes:

page_views = { |hash, key| hash[key] = { count: 0, url: key } }
page_views[""][:count] += 1
page_views # => {""=>{:count=>1, :url=>""}}

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"]}