rubyguides

Hash#value?

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

That makes it a useful membership check when the value matters more than the key. It is especially handy in validation code or in small lookups where you only need to know whether the data exists somewhere in the hash.

Syntax

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

Parameters

ParameterTypeDefaultDescription
valueObjectRequiredThe value to search for in the hash

Return Value

Returns true if value exists as a value in the hash, false if it does not.

Description

value? checks whether the given object appears as a value in the hash. Unlike key?, which is O(1), value? must scan all values in the hash — O(n) time.

The method has one alias: has_value?.

Since the collection can hold the same value under multiple keys, value? only tells you that the value exists somewhere, not which key or keys hold it. Use key? or fetch to find or access the associated key.

That distinction matters when duplicate values are possible, because the method answers the question “is it there?” rather than “where is it?”. If you need the location, a second pass is still required.

Examples

Basic usage

scores = { alice: 95, bob: 82, carol: 95 }

scores.value?(95)   # => true
scores.value?(82)  # => true
scores.value?(70)  # => false

The basic check is straightforward — pass the value and get a boolean back. Because the method uses == rather than equal?, it works correctly with integers, symbols, and other objects that may have the same content but different object identities. This matters when you are checking for computed values rather than object references.

Detecting sentinel values

config = { timeout: nil, retries: 3, debug: false }

config.value?(nil)     # => true
config.value?(false)  # => true — even falsy values are found
config.value?(0)      # => false

Sentinel checks with nil and false deserve special attention because both are falsy in Ruby. The method still finds them correctly — it does not confuse falsiness with absence — so you can safely test for nil or false as legitimate values without risk of a false negative. This makes it useful for configuration hashes where a key being set to false means something different from the key being missing entirely.

Finding keys that share a value

Because value? does not reveal which keys hold a value, you need to search manually to find them:

inventory = { apples: 0, bananas: 0, grapes: 5 }

zero_items = inventory.select { |_k, v| v.zero? }.keys
# => [:apples, :bananas]

inventory.value?(0)   # => true

When the same value appears under several keys, value? confirms the value exists but does not surface the keys. That is the right behavior when you only need the yes/no answer — the method stays fast because it stops as soon as it finds the first match rather than scanning the entire collection. If you need the matching keys as well, fall back to select or filter with a block that checks values.

With mixed value types

mixed = { count: 1, ratio: 1.0, percent: "100%" }

mixed.value?(1)       # => true  — integer 1
mixed.value?(1.0)    # => true  — Float and Integer compare equal
mixed.value?("1")     # => false — string "1" is distinct

Performance

value? is O(n) — it checks every value in the hash until it finds a match. For large hashes this is slower than key?, which is O(1) on average.

For repeated checks, the linear scan can add up quickly. In that case a Set or a different data shape may be a better fit, especially if you care more about repeated membership tests than about the original hash layout.

If you need to call value? repeatedly on the same hash, build a set of values first.

Converting the values to a Set is a one-time O(n) cost that pays off when you plan to test membership many times over the same data. After the initial build, each include? call runs in O(1) average time, which turns a series of linear scans into a series of constant-time checks. For long-lived objects or hot paths, that upfront investment keeps the runtime predictable.

value_set = Set.new(hash.values)
value_set.include?(target_value)  # O(1) after the initial O(n) build

This pattern works well when the values are static or change less often than the lookups. If the underlying hash changes between checks, you need to rebuild the set or decide whether the extra bookkeeping is worth the speedup. For small hashes — under a few hundred entries — the linear scan is usually fast enough that the added complexity of a set is not justified.

Gotchas

Confusing value? with key?. value? checks values; key? checks keys:

scores = { alice: 95, bob: 82 }

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

Duplicate values. When several keys point to the same value, value? confirms that value is present but does not reveal how many keys or which ones. If you need the mapping in reverse — value to keys — you will need to iterate and collect the keys yourself since the method only answers the membership question. It stops at the first match, so it cannot tell you about additional occurrences of the same value further down in the collection.

h = { a: nil, b: nil }

h.value?(nil)    # => true
h.key?(nil)      # => false — nil is a value, not a key

Use select with a block to find which keys hold a specific value.

See Also