Hash#has_key?
These four methods check whether a hash contains a specific key. They are aliases for each other — you can use whichever reads most naturally in your code.
They are especially useful when nil is a valid stored value, because bracket lookup cannot tell the difference between a missing key and a key whose value happens to be nil. A boolean existence check avoids that ambiguity.
That makes these methods a safer choice in conditional logic, validation steps, and code that needs to answer a yes-or-no question without caring about the stored value itself. They keep the intent of the check obvious to the reader.
Syntax
hash.has_key?(key) # => true or false
hash.key?(key) # => true or false
hash.include?(key) # => true or false
hash.member?(key) # => true or false
Parameters
| Parameter | Type | Description |
|---|---|---|
key | Object | The key to search for in the hash |
Return Value
Returns true if the hash contains the given key, false otherwise.
Description
Ruby provides four identical methods for checking key existence:
has_key?— The most descriptive name. Readable in tests and conditionals.key?— Concise. Common in Rails and Ruby codebases.include?— Familiar if you come from Python or C++.member?— The oldest alias, dating back to Ruby 1.8.
All four methods do exactly the same thing. Choose based on readability in your specific context.
The key difference between these methods and bracket notation (hash[key]) is that bracket notation returns nil for missing keys, which is ambiguous — you cannot tell whether the key exists with a nil value or the key is missing entirely. These methods always return a boolean.
Examples
Basic key checking
user = { name: "Alice", age: 30, city: "London" }
user.has_key?(:name) # => true
user.has_key?(:email) # => false
user.key?(:age) # => true
The return value is always a plain boolean, which makes these methods ideal for if statements and guard clauses. There is no ambiguity about what a truthy or falsy response means — true confirms the key exists, and false means it does not. This is the main advantage over bracket notation when nil is a legitimate stored value.
Using in conditionals
config = { environment: "production", debug: false }
if config.key?("environment")
puts "Running in #{config["environment"]} mode"
end
# => Running in production mode
Notice that the key check and the value lookup are separate steps here. First the code confirms the key is present with key?, then it retrieves the value with bracket notation. Splitting the check from the retrieval makes the intent explicit and avoids the ambiguity that would arise if nil were a possible stored value for the "environment" key.
Different aliases, same result
data = { a: 1, b: 2 }
data.has_key?(:a) # => true
data.key?(:a) # => true
data.include?(:a) # => true
data.member?(:a) # => true
All four aliases are interchangeable — they share the same implementation and return the same result. The choice between them is purely stylistic. key? is the most concise and is widely used in modern Ruby codebases, while has_key? is the most self-documenting and reads well in conditional expressions. Pick the one that fits the surrounding code style.
With string keys
params = { "controller" => "users", "action" => "show" }
params.has_key?("controller") # => true
params.has_key?(:controller) # => false - different key type!
# Convert string keys to symbols if needed
params.transform_keys(&:to_sym).has_key?(:controller)
# => true
Note that symbol keys and string keys are distinct in Ruby hashes. A symbol like :controller and a string like "controller" are different objects as far as the hash is concerned. If you are working with data from an external source such as JSON or HTTP parameters, the keys will typically be strings, and you need to use string lookups or convert the keys before using symbol-based checks.
Comparison with bracket notation
scores = { alice: nil, bob: 100 }
scores[:alice] # => nil (ambiguous!)
scores[:charlie] # => nil (also nil!)
scores.has_key?(:alice) # => true - knows the key exists
scores.has_key?(:charlie) # => false - key is missing
This example makes the ambiguity visible. Both scores[:alice] and scores[:charlie] return nil, but for different reasons — one key exists with a nil value, the other does not exist at all. A boolean key check separates these two cases cleanly. This distinction comes up frequently in APIs, configuration files, and any data structure where the absence of a key carries a different meaning from a key whose value happens to be nil.
Common Patterns
safe value retrieval
settings = { theme: "dark" }
# Instead of this:
theme = settings[:theme] || "light"
# Do this (cleaner):
theme = settings.fetch(:theme, "light")
fetch combines the existence check with a default fallback in one call. It raises KeyError when the key is missing and no default is provided, which makes it ideal for required configuration where a missing key should be treated as a failure rather than silently returning nil. This pattern removes the need for a separate key? check before access.
validation
required_keys = [:name, :email, :password]
user_input = { name: "Bob", email: "bob@example.com" }
required_keys.each do |key|
unless user_input.key?(key)
raise ArgumentError, "Missing required key: #{key}"
end
end
# Raises: Missing required key: password
This pattern is common in form handlers and API endpoints where a fixed set of keys must be present before the payload is processed. By collecting all missing keys before raising an error, you can give the caller a complete list of what is absent rather than forcing them to fix one field at a time through repeated failures. For larger sets of required keys, consider storing them in a constant or configuration file so the validation logic stays separate from the key list itself.
Performance
These methods run in O(1) average time because hashes use internal lookup tables. The time complexity does not grow with hash size.
See Also
hash-fetch— Fetch a value with a default or blockhash-dig— Safely access nested values