Array#each_with_object
each_with_object(memo) { |element, memo| ... } → memo Array#each_with_object walks an array one element at a time and hands you a mutable object alongside each element. Mutate that object inside the block and the same object comes back when the loop finishes. The block’s return value is ignored entirely.
Strictly speaking, the method is defined on Enumerable and just inherited by Array. This page covers it as it behaves on an Array receiver, which is where it gets used most often. For the module-level view, see Enumerable#each_with_object.
Syntax
arr.each_with_object(memo) { |element, memo| ... } # => memo
arr.each_with_object(memo) # => Enumerator (no block given)
The block receives two arguments: the current element first, the memo second. That order is the opposite of inject and reduce, and getting it wrong is the single most common bug with this method.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
memo | Object | Yes | The accumulator. Returned unchanged at the end. Usually [], {}, or a Hash.new with a default block for auto-vivification. |
| Block | { |element, memo| ... } | Yes (in practice) | Runs once per element. Must mutate memo in place — the return value is discarded. |
What comes back
The exact object you passed in. For an empty array the block never runs and the memo is returned untouched, which is one place each_with_object is safer than inject (no nil surprise).
Building a hash from a list
The common pattern: start with an empty hash and assign keys inside the block.
words = ["apple", "banana", "cherry"]
lengths = words.each_with_object({}) { |word, hash| hash[word] = word.length }
lengths
# => {"apple"=>5, "banana"=>6, "cherry"=>6}
Frequency tally without tally
Hash.new(0) makes += safe on a missing key, so you can count occurrences in one pass. The default value of 0 is what lets tally[score] += 1 work even on a key the hash has never seen before, and it also sets the starting count for brand-new buckets.
scores = [10, 20, 10, 30, 20, 10, 40]
counts = scores.each_with_object(Hash.new(0)) { |score, tally| tally[score] += 1 }
counts
# => {10=>3, 20=>2, 30=>1, 40=>1}
If you only need equality counts, Array#tally does this in one method call. Reach for each_with_object when the counts need extra work (weighting, bucketing, combining with other data).
Grouping with an auto-vivifying hash
Hash.new { |h, k| h[k] = [] } creates the bucket on first read, so you can group without checking has_key? first.
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
grouped = numbers.each_with_object(Hash.new { |h, k| h[k] = [] }) do |n, hash|
n.even? ? hash[:even] << n : hash[:odd] << n
end
grouped
# => {:even=>[2, 4, 6, 8, 10], :odd=>[1, 3, 5, 7, 9]}
Building a transformed array
This is the simplest use of the method: an array goes in, an array comes out, with each element transformed. Start with [], push into it, and return the result. It is a one-line pattern, but the block still has to mutate the memo, which is why arr << n * 2 works and a bare n * 2 would silently drop every value.
numbers = [1, 2, 3, 4, 5]
doubled = numbers.each_with_object([]) { |n, arr| arr << n * 2 }
doubled
# => [2, 4, 6, 8, 10]
This is fine, but for “one transformed value per input” cases, Array#map reads more directly. The two-line version with map is what most Ruby developers reach for first, and you should default to it whenever the output has the same shape as the input.
doubled = numbers.map { |n| n * 2 }
# => [2, 4, 6, 8, 10]
Use each_with_object for the array case when the block also needs to feed something else (an index lookup, a parallel hash, a counter) and you want both in a single pass.
each_with_object vs inject / reduce
They iterate the same way and pass a running value alongside each element, so they get mixed up. The differences:
inject / reduce | each_with_object | |
|---|---|---|
| Block argument order | |memo, element| | |element, memo| (reversed) |
| Block return value | Becomes the next memo | Discarded — ignored entirely |
| How the memo advances | Return a new value | Mutate the memo in place |
| Final return | The last block return value | The exact object passed in |
| Empty array | Returns nil (or raises) | Returns the memo untouched |
Reach for inject when you are computing a scalar (a sum, a max, a concatenated string). Reach for each_with_object when you are filling a mutable collection.
nums = [1, 2, 3, 4]
# inject — return value drives the next accumulator
nums.inject(0) { |sum, n| sum + n }
# => 10
# each_with_object — mutate the memo
nums.each_with_object([]) { |n, arr| arr << n * 2 }
# => [2, 4, 6, 8]
Gotchas
The block return value is thrown away. Returning a new object from the block does nothing. The memo is only updated when you mutate it with <<, []=, +=, concat, or any other in-place method.
The argument order is reversed from inject. Copy-paste between the two and the variables swap silently. The code runs, but it does the wrong thing.
No block returns an Enumerator. Useful for chaining, but rarely the reason you reach for this method:
[1, 2, 3].each_with_object([])
# => #<Enumerator: ...>
See Also
- Array#inject: the reduction sibling; same iteration, different accumulator semantics.
- Array#reduce: alias of
inject, same comparison story. - Enumerable#each_with_object: the module-level treatment of the same method.