rubyguides

Hash#key?

The key? method returns true if the given key is present in the hash, false otherwise.

Syntax

hash.key?(key)   # => true or false

Parameters

ParameterTypeDefaultDescription
keyObjectRequiredThe key to look up in the hash

Return Value

Returns true if key exists in the hash, false if it does not.

Description

key? is one of four identical methods for checking key existence on Ruby’s Hash. The others are has_key?, include?, and member?. All four do the same thing — they use the hash’s internal lookup, which is O(1) on average.

Use key? when you want to verify a key is present before accessing or modifying its value. This is safer than direct access with [], which returns nil for missing keys (unless the hash has a default).

Examples

Basic usage

config = { host: "localhost", port: 5432, ssl: true }

config.key?(:host)    # => true
config.key?(:timeout) # => false

Guarding against missing keys

def connect(options)
  raise ArgumentError, "host is required" unless options.key?(:host)

  host = options[:host]
  port = options.fetch(:port, 5432)
  # ...
end

key? is often paired with fetch when you want to distinguish between a missing key and an explicit nil value:

settings = { debug: false }

if settings.key?(:debug)
  # key exists, even if the value is falsy
  enable_debugging if settings[:debug]
end

With symbol and string keys

Symbol and string keys are distinct in Ruby hashes:

prefs = { theme: "dark", "font_size" => 14 }

prefs.key?(:theme)        # => true
prefs.key?("theme")       # => false
prefs.key?("font_size")   # => true

Checking before assignment

defaults = { timeout: 30, retries: 3 }
user_options = { timeout: 60 }

def merge_options(defaults, overrides)
  merged = defaults.dup
  overrides.each do |key, value|
    merged[key] = value if merged.key?(key)
  end
  merged
end

merge_options(defaults, user_options)
# => { timeout: 60, retries: 3 }

Only keys that exist in the defaults are taken from overrides.

Aliases

key? is equivalent to:

  • has_key? — same behavior, more explicit
  • include? — same behavior, older name
  • member? — same behavior, oldest alias
h = { a: 1, b: 2 }

h.key?(:a)        # => true
h.has_key?(:a)    # => true
h.include?(:a)    # => true
h.member?(:a)     # => true

Gotchas

Confusing key? with value?. key? checks keys. value? checks values. These are independent lookups:

scores = { alice: 95, bob: 82 }

scores.key?(:alice)   # => true  — :alice is a key
scores.value?(95)     # => true  — 95 is a value
scores.key?(95)       # => false — 95 is not a key
scores.value?(:alice) # => false — :alice is not a value

Using ||= with potentially nil values. If a key exists with an explicit nil value, key? returns true but ||= still overwrites:

config = { debug: nil }

config.key?(:debug)        # => true
config[:debug] ||= false   # overwrites nil with false — probably not what you want

Use fetch instead when you need to distinguish between a missing key and nil.

See Also