rubyguides

Hash#each_pair

hash.each_pair { |key, value| block } -> hash

Hash#each_pair is an alias for Hash#each. Both methods iterate over key-value pairs and yield each key and value to the block. The method returns the original object, making it chainable with other Enumerable methods.

The method is a good fit when you want the code to signal clearly that both the key and the value carry meaning. It is a small naming choice, but it can make hash iteration read a little more explicitly when you are working with blocks that use both the key and the value.

basic usage

user = { name: "Alice", age: 30, city: "London" }

user.each_pair do |key, value|
  puts "#{key}: #{value}"
end
# Output:
# name: Alice
# age: 30
# city: London

The block receives two parameters: the key and the value. Both are available for use inside the block.

That turns each_pair into a simple walk through a hash when both pieces of data are relevant. The block stays focused on formatting, filtering, or accumulation, instead of dealing with the hash structure itself.

destructuring in blocks

Ruby 2.7+ supports block parameter destructuring, which lets you unpack the key-value pair directly:

data = { a: 1, b: 2, c: 3 }

data.each_pair do |(key, value)|
  puts "#{key} = #{value}"
end
# Output:
# a = 1
# b = 2
# c = 3

A destructured block is especially handy when the pair needs to be unpacked before any other logic runs. It also keeps the parameter list compact, which helps when the block body already has several moving parts.

Using destructuring is particularly useful when working with each_with_object or when you prefer explicit unpacking. A destructured block can make the body easier to read and keeps the line that introduces the block visually tidy.

each vs each_pair

Hash#each and Hash#each_pair are functionally identical. Ruby provides both for historical reasons and developer preference. Some codebases use each_pair to explicitly indicate that both keys and values are being processed, while others prefer the shorter each form.

# These produce identical results
hash = { x: 1, y: 2 }

hash.each { |k, v| puts "#{k}: #{v}" }
hash.each_pair { |k, v| puts "#{k}: #{v}" }

Both methods return the original object, so you can chain them:

This short chain is useful when you want the iteration step to stay visible, but you still need a transformed result at the end. It keeps the work in one expression and makes the data shape easy to follow step by step.

result = { a: 1, b: 2, c: 3 }
  .each_pair.select { |k, v| v > 1 }
  .to_h

# => { b: 2, c: 3 }

A small extra step is worth it when you want to keep the key-value walk explicit but still finish with a transformed result. It gives the reader one short pipeline to follow instead of splitting the work across several separate statements. The line break also makes it easier to see where iteration ends and the final shape of the data begins.

A chained call is useful when you want to keep the iteration step and the filtering step in one expression. The intermediate hash is still readable, and the resulting array of pairs can be turned back into a hash without any extra bookkeeping.

Choosing between each and each_pair is mostly stylistic. Many readers find each_pair a touch more explicit, while each is shorter and already familiar from Enumerable.

As you chain each_pair, the receiver still behaves like the original collection until the next Enumerable step takes over. That keeps the flow easy to follow because the iteration intent and the selection intent stay close together in the same expression.

Because the method does not hide the pair structure, the next Enumerable call can work with the same key and value values without any extra unpacking.

In a review or debugging session, saying “each pair” reminds you that the block receives two values at a time, which can be easier to follow than a more generic iteration name when the surrounding code is busy.

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

scores.each_pair.select { |_, score| score > 80 }
# => { alice: 95, bob: 82 }

With Enumerable

Since Hash includes Enumerable, you can use each_pair in combination with other Enumerable methods:

This quick example keeps the focus on the chain itself instead of the surrounding setup. It shows how the returned hash can flow straight into another Enumerable step without changing the overall shape of the expression.

That shape is helpful when the next step still wants a hash instead of an array. You can keep the iteration visible, then hand the filtered result to another method without changing the data type first. It also makes the chain easier to scan because the selection is obvious before the next transformation starts.

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

high_scorers = scores
  .each_pair
  .select { |_, score| score > 80 }
  .map { |name, _| name }

# => [:alice, :bob]

That chain is a good reminder that hashes participate in the wider Enumerable API. Once the pairs are being yielded, the usual selection and mapping tools are available.

Return Value

Each_pair returns the original hash, not the block’s return value. If you need to collect results, consider using map or each_with_object instead:

hash = { a: 1, b: 2, c: 3 }

# Returns hash, not the summed value
returned = hash.each_pair { |k, v| puts "#{k}: #{v}" }
# => { a: 1, b: 2, c: 3 }

# Use each_with_object for accumulation
sum = hash.each_pair.with_object(0) { |(k, v), acc| acc + v }
# => 6

The accumulation example is intentionally simple, because the real lesson is about return value. each_pair is about iteration, not aggregation, so pairing it with with_object or a different Enumerable helper makes the intent clearer.

ruby version notes

This method has been available since Ruby 1.8. Block parameter destructuring requires Ruby 2.7 or later. Ruby 3.x maintains full backward compatibility with all existing behavior.

See Also

  • hash-each — The primary iteration method, identical to each_pair
  • hash-merge — Combine hashes with merge
  • hash-select — Filter hash entries based on conditions