rubyguides

Hash#fetch_values

Fetch_values returns an array containing the values for the given keys. It behaves like values_at but differs in how it handles missing keys, it raises KeyError by default instead of returning nil, and it accepts an optional block for handling missing keys.

That makes it a good fit when you want to read a few known keys and treat missing data as a real problem. The block form gives you a second option when the missing-key case should be handled inline instead of raising immediately.

basic usage

h = { foo: 0, bar: 1, baz: 2 }

h.fetch_values(:baz, :foo)
# => [2, 0]

Unlike values_at, missing keys raise KeyError instead of returning nil:

That difference matters when a missing entry should stop the work immediately. A strict lookup is often easier to debug than a partially filled result because the failure happens at the point where the data is first needed.

In practice, that gives you a simpler failure mode and a clearer debugging trail when the input is incomplete.

h = { a: 1, b: 2 }

h.values_at(:a, :z)
# => [1, nil]

h.fetch_values(:a, :z)
# KeyError: key not found: :z

That strict behavior is the main difference from values_at. It is a good fit when the caller expects every lookup to succeed, because a missing key becomes obvious right away instead of turning into a quiet placeholder.

That example shows the strict lookup path in the smallest possible form. The method either finds every key or stops immediately, which is exactly what you want when a partial result would be misleading.

That contrast is easiest to remember if you think of fetch_values as the stricter sibling of values_at. It is for the cases where a missing key should be visible immediately, not quietly replaced with nil.

That contrast is the whole reason to reach for fetch_values. It behaves like a stricter version of values_at, which can be exactly what you want when missing values should not slip through silently.

signature

fetch_values(*keys) { |key| block }

Accepts one or more keys and returns an array of values in the same order. When a key is not found, raises KeyError unless a block is given.

handling missing keys with a block

Pass a block to provide a fallback value for missing keys:

h = { alice: 95, bob: 87 }

result = h.fetch_values(:alice, :carol) { |k| "unknown: #{k}" }
# => [95, "unknown: carol"]

The block is called only for keys that are not found. Keys that exist return their actual values.

The result is still a plain array, so the return value stays easy to pass to other methods after the lookup finishes. That keeps the feature useful even when the fallback path is only a small part of a longer pipeline.

h = { x: 10, y: 20 }

h.fetch_values(:x, :z) { |k| k.to_s.upcase }
# => [10, "Z"]

That second example also shows how the block can adapt to the missing key itself. By looking at the key name, the fallback can build a string, a sentinel, or any other value that fits the surrounding code.

When the block is present, the method still keeps the original values for the keys that exist. Only the missing ones flow through the fallback path, which makes the result easy to reason about.

This makes fetch_values useful when you want different fallback behavior than nil but do not want to wrap every call in a begin/rescue block.

It is a nice compromise when some keys are required and others are optional. The block can calculate a fallback on the fly while still keeping the call compact.

You can also use that block to normalize missing values into a sentinel, a default string, or another value that your code already understands. That keeps the lookup and fallback decision together in one expression.

The pattern is especially readable when the keys are already known in advance. You can glance at the call, see the required names, and understand whether the code expects a hard failure or a computed substitute.

comparison with values_at

MethodMissing key behavior
values_at(*keys)Returns nil for missing keys
fetch_values(*keys)Raises KeyError
fetch_values(*keys) { block }Calls block for missing keys

Use values_at when missing keys are expected and nil is acceptable. Use fetch_values when missing keys should be an error, or when you want to compute a fallback value inline with a block.

The difference is mostly about intent. values_at says “missing is okay,” while fetch_values says “missing should be handled now.”

That split is useful in code reviews because it tells you what kind of data contract the caller expects. A permissive lookup belongs in one place, while a strict lookup belongs in another, and the method choice makes that expectation visible.

If you want a compact contrast, this kind of pair is easy to read at the call site:

settings = { timeout: 30, retries: 2 }

settings.values_at(:timeout, :retries)
# => [30, 2]

settings.fetch_values(:timeout, :retries)
# => [30, 2]

The two lines look similar when the keys exist, but the error handling story is different when one of them is absent. That makes the method choice meaningful even before you reach for a block. A compact sample like this is also easier to compare in a diff, because the strict behavior is implied by the name instead of repeated in the output.

Adding fetch_values in a nearby call would make the contrast even clearer in prose, but the point here is the contract, not the happy-path output. The method choice signals whether a missing key should remain visible or be folded into a fallback.

real-world example

Fetching specific fields from a configuration hash with a fallback:

config = {
  host: "localhost",
  port: 5432,
  user: "app",
  password: ENV["DB_PASSWORD"]
}

# Raise an error if required keys are missing
required = [:host, :port, :user, :password]
config.fetch_values(*required)
# KeyError if password env var is not set

That example shows the strict mode clearly, because a missing required key should stop the program instead of drifting through with a partial configuration. The method makes that requirement explicit at the call site.

The block form still fits neatly into the same mental model when you need defaults for only some positions. In other words, the method can express both “fail loudly” and “fill in a reasonable value” without changing the overall shape of the call.

With a block for optional keys:

optional = [:timeout, :pool_size, :ssl_mode]

config.fetch_values(:host, :port, *optional) { |k| nil }
# Returns values for host, port, and nil for missing optional keys

The block form lets you keep the call compact even when only some keys are optional. That is useful when the same hash contains both required settings and best-effort defaults.

chaining with dig

Since fetch_values returns an array, you can chain array methods on the result:

scores = { alice: 95, bob: 87, carol: 92 }

scores.fetch_values(:alice, :bob, :carol).map(&:to_f)
# => [95.0, 87.0, 92.0]

Once the values are returned as an array, you can keep chaining ordinary array methods. That makes fetch_values easy to fit into a larger data-processing pipeline.

When the surrounding code already treats the result as a list, this is often the cleanest handoff point. The hash lookup stays focused on retrieval, and the next step can stay focused on transforming the array that came back.

See Also