Hash#dig

hash.dig(*keys) -> value or nil
Returns: object or nil · Added in v2.3 · Updated March 13, 2026 · Hash Methods
ruby hash methods nested

The .dig method safely retrieves nested values from hashes and arrays. It walks through the structure using each argument as a key, returning nil if at any point a key is missing instead of raising an error.

Signature

hash.dig(key, *keys) → value or nil

Parameters

ParameterTypeDescription
keyObjectThe first key to look up
*keysObjectAdditional keys for nested access

Return Value

Returns the value at the nested key path, or nil if any key in the path is missing.

Basic Usage

user = {
  name: "Alice",
  address: {
    city: "London",
    zip: "SW1A 1AA"
  }
}

user.dig(:address, :city)    # => "London"
user.dig(:address, :country) # => nil
user.dig(:phone)             # => nil

The method stops at the first missing key and returns nil, never raising an error.

Working with Arrays

.dig works with arrays too, using integer indices:

data = {
  users: [
    { name: "Alice", scores: [95, 87] },
    { name: "Bob", scores: [78, 92] }
  ]
}

data.dig(:users, 0, :name)     # => "Alice"
data.dig(:users, 1, :scores, 0) # => 78
data.dig(:users, 5, :name)    # => nil
data.dig(:users, 0, :age)     # => nil

Negative indices work as expected:

data.dig(:users, -1, :name)   # => "Bob"

The Problem .dig Solves

Before .dig (introduced in Ruby 2.3), you had to manually check each level:

# Old way - tedious and error-prone
if user[:address] && user[:address][:city]
  city = user[:address][:city]
end

# With dig - clean and safe
city = user.dig(:address, :city)

You could also use the && operator, but it gets unwieldy with deep nesting:

# Works but hard to read with multiple levels
city = user[:address] && user[:address][:city] && user[:address][:city][:name]

Common Use Cases

Configuration Reading

config = {
  database: {
    production: {
      host: "db.example.com",
      port: 5432
    }
  }
}

config.dig(:database, :production, :host)
# => "db.example.com"

config.dig(:database, :staging, :host)
# => nil

API Response Parsing

response = {
  data: {
    user: {
      profile: {
        avatar_url: "https://example.com/avatar.png"
      }
    }
  }
}

response.dig(:data, :user, :profile, :avatar_url)
# => "https://example.com/avatar.png"

Default Values with nil Coalescing

city = user.dig(:address, :city) || "Unknown"

Comparison with Bracket Notation

AspectBracket Notation.dig
Missing keyRaises KeyErrorReturns nil
Deep nestingManual checks neededSingle call
ReadabilityDegrades with depthStays clean
# Bracket notation - raises error
user[:address][:city] # => KeyError if :address missing

# dig - safe
user.dig(:address, :city) # => nil

See Also

  • hash-fetch — Fetch a value with a default or block
  • hash-each — Iterate over key-value pairs