Hash#each_with_object
Overview
each_with_object iterates over a hash, yielding each key-value pair and an accumulator object to the block. Unlike each, which returns the original hash, each_with_object returns whatever object you pass in — making it useful for transforming a hash into a different data structure.
The method is actually defined on Enumerable, so it works on any enumerable, including hashes. When iterating over a hash, the block receives key-value pairs as a two-element array as the first argument.
Signature
each_with_object(obj) { |(key, value), memo| block } -> obj
each_with_object(obj) -> Enumerator
Parameters
| Parameter | Description |
|---|---|
obj | The initial value of the accumulator — any object. Returned after iteration completes. |
Return Value
Returns the passed obj after processing all entries. The original hash is unchanged.
Basic Usage
Building an array from a hash
scores = { alice: 95, bob: 82, carol: 78 }
result = scores.each_with_object([]) do |(name, score), arr|
arr << "#{name}: #{score}"
end
# => ["alice: 95", "bob: 82", "carol: 78"]
Building a new hash
items = { a: 1, b: 2, c: 3 }
doubled = items.each_with_object({}) do |(k, v), h|
h[k] = v * 2
end
# => { a: 2, b: 4, c: 6 }
Using a counter
words = { cat: true, dog: true, elephant: true }
count = words.each_with_object(0) do |(word, _), n|
n += word.length
end
# count is the total length of all words (returns the final accumulator)
Common Use Cases
Grouping by a condition
users = { alice: 28, bob: 17, carol: 35, dave: 22 }
by_age_group = users.each_with_object({ "under 25" => [], "25+" => [] }) do |(name, age), groups|
if age < 25
groups["under 25"] << name
else
groups["25+"] << name
end
end
# => { "under 25" => [:bob, :dave], "25+" => [:alice, :carol] }
Flattening keys or values
config = { db: { host: "localhost", port: 5432 }, cache: { host: "redis", port: 6379 } }
flat = config.each_with_object({}) do |(section, values), result|
values.each do |key, value|
result["#{section}_#{key}".to_sym] = value
end
end
# => { :db_host => "localhost", :db_port => 5432, :cache_host => "redis", :cache_port => 6379 }
Collecting specific fields
products = {
widget: { price: 10, weight: 5, stock: 100 },
gadget: { price: 25, weight: 2, stock: 50 },
gizmo: { price: 15, weight: 8, stock: 75 }
}
summary = products.each_with_object([]) do |(name, attrs), list|
list << { name: name, value: attrs[:price] * attrs[:stock] }
end
# => [{ name: :widget, value: 1000 }, { name: :gadget, value: 1250 }, { name: :gizmo, value: 1125 }]
each_with_object vs inject
Both accumulate a result across iterations. The difference is subtle:
# inject passes the return value of the block as the accumulator for the next iteration
scores.inject({}) do |memo, (name, score)|
memo[name] = score > 80 ? "pass" : "fail"
memo # must explicitly return memo
end
# each_with_object passes the same object every time — no need to return it
scores.each_with_object({}) do |(name, score), memo|
memo[name] = score > 80 ? "pass" : "fail"
end
each_with_object is cleaner when the accumulator is a collection you’re mutating directly. inject is more flexible — the block’s return value becomes the next accumulator, which is useful for arithmetic chains.
Gotchas
Block argument order. The first block argument is the key-value pair as a two-element array, the second argument is the memo object:
h.each_with_object({}) do |(key, value), memo| # correct
# NOT: |key, value, memo|
end
Forgetting the parentheses around key, value is a common mistake — without them, key receives the pair [:a, 1] and value is the memo object.
Returns the passed object, not a new one. If you pass a hash as the accumulator and mutate it, the original hash is affected:
original = {}
result = { a: 1 }.each_with_object(original) do |(k, v), memo|
memo[k] = v * 2
end
result.object_id == original.object_id # => true
result # => { a: 2 }
original # => { a: 2 } (same object!)
See Also
- /reference/enumerable/enumerable-each-with-object/ — the Enumerable version (works on arrays and any enumerable)
- /reference/hash-methods/each/ — basic hash iteration without accumulation
- /guides/ruby-hash-tricks/ — practical hash patterns and transformations