rubyguides

Hash#default

hash.default(key = nil) -> object

Description

Hash#default returns the default value for a hash — the value returned when you access a key that doesn’t exist. Every hash starts with a default of nil. You can set a default when creating the hash with Hash.new, or change it later with Hash#default=.

A map with a default value works well for counting, memoization, and any situation where you would otherwise need to check if a key exists before using it.

setting a default at creation

h = Hash.new(0)
h[:count]          # => 0
h[:count] += 1
h[:count]           # => 1

The first example uses Hash.new(0) to create a counter that starts at zero for every missing key. This technique is common in tallying scenarios where you want to increment counts without checking for key existence first. The integer default is a safe starting point that you can add to immediately.

h = Hash.new("unknown")
h[:name]           # => "unknown"

Choosing a default at creation time is often the simplest option when every missing key should behave the same way. This pattern shows up in counters, caches, and reports where a neutral starting value keeps the code easy to follow.

setting a default after creation

h = {}
h.default = -1
h[:missing]        # => -1

Changing the default later is useful when the data structure already exists and you only need to adjust the fallback behavior. It avoids rebuilding the entire collection just to change one piece of metadata, which is especially helpful when the map has already been populated with many entries.

getting the current default

h = Hash.new
h.default          # => nil

h = Hash.new(99)
h.default          # => 99

Reading the current default helps when code receives a map from an external source. The lookup tells you whether the map is using a plain value or still relying on the default nil.

default proc vs default value

When you use a block with Hash.new, Ruby stores a proc instead of a plain value. The proc receives the hash and the missing key, and its return value becomes the default.

h = Hash.new { |hash, key| hash[key] = [] }
h[:foo] << 1
h[:foo] << 2
h[:bar]            # => []
h                  # => {:foo=>[1, 2], :bar=>[]}

Reach for the proc version when the default should create fresh containers. It lets each missing key stand on its own instead of sharing a single object across all lookups.

With a plain default value, all missing keys return the same object:

h = Hash.new([])        # shared empty array — dangerous
h[:a] << 1
h[:b]                   # => [1] - same array!
h                      # => {} - nothing was stored

That shared-object behavior is the risky part of Hash.new([]): every missing key points at the same array. If you are building lists or buckets, that can lead to confusing cross-talk between keys.

With a proc, each missing key gets its own result:

h = Hash.new { [] }
h[:a] << 1
h[:b]                   # => [] - different array
h                      # => {} - still nothing stored, because the proc did not store

Notice that the proc only returns a value, it does not write anything back into the hash unless you explicitly store it. That is why the next example assigns into hash[key] inside the block.

To auto-store the proc result, reference the key explicitly inside the block:

h = Hash.new { |hash, key| hash[key] = [] }
h[:items] << "first"
h[:items] << "second"
h[:items]               # => ["first", "second"]

Once the proc stores the value, the hash behaves much more like a nested container builder. This pattern is common when grouping items by key or when accumulating values for later processing.

the default method with an argument

When called with a key argument, default(key) checks whether the key exists and returns the default only if the key is truly missing — not just if its value is nil.

h = { foo: nil, bar: 1 }
h[:foo]                # => nil
h.default(:foo)        # => nil - key exists even though value is nil
h.default(:baz)        # => nil - key is missing, returns the default

The subtle difference matters in practice. A present key with a nil value does not mean the same thing as a missing key, and default(key) lets you ask which case you are dealing with.

See Also

  • Hash#fetch — raises KeyError for missing keys instead of returning a default
  • Hash#dig — safely access nested hash values
  • Hash#has_key? — check if a key exists in the hash