Enumerable#each_with_object

Returns: Object · Added in v1.8.6 · Updated March 15, 2026 · Enumerable
ruby enumerable iteration accumulator

The each_with_object method iterates over a collection, passing each element and a mutable object you provide. Unlike inject/reduce, the accumulator object is returned unchanged at the end — you modify it inside the block.

How It Works

Unlike inject/reduce where the return value of the block becomes the accumulator, each_with_object keeps your object intact and lets you mutate it directly:

result = collection.each_with_object(initial_object) { |element, accumulator|
  # modify accumulator in place
}

The object you pass is the one that gets returned at the end.

Basic Usage

Building an array of doubled values:

numbers = [1, 2, 3, 4, 5]
doubled = numbers.each_with_object([]) { |n, arr| arr << n * 2 }
# => [2, 4, 6, 8, 10]

Building a hash from an array:

words = ["apple", "banana", "cherry"]
lengths = words.each_with_object({}) { |word, hash| hash[word] = word.length }
# => {"apple"=>5, "banana"=>6, "cherry"=>6}

Why Use Each_with_object Over Inject?

The key difference is mutability. With inject/reduce, the accumulator is reassigned each iteration:

# inject - accumulator is reassigned each time
[1, 2, 3].inject(0) { |acc, n| acc + n }
# => 6 (acc gets new value each iteration)

With each_with_object, you start with a specific object and mutate it:

# each_with_object - you control the exact object returned
[1, 2, 3].each_with_object([]) { |n, arr| arr << n }
# => [1, 2, 3] (the exact array you passed in, now populated)

Practical Examples

Grouping Elements by a Criteria

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
grouped = numbers.each_with_object({ even: [], odd: [] }) do |n, hash|
  n.even? ? hash[:even] << n : hash[:odd] << n
end
# => {even=>[2, 4, 6, 8, 10], odd=>[1, 3, 5, 7, 9]}

Building a Nested Hash

orders = [
  { customer: "Alice", item: "Book", price: 15 },
  { customer: "Bob", item: "Pen", price: 3 },
  { customer: "Alice", item: "Pen", price: 2 }
]

by_customer = orders.each_with_object(Hash.new { |h, k| h[k] = [] }) do |order, hash|
  hash[order[:customer]] << { item: order[:item], price: order[:price] }
end
# => {"Alice"=>[{:item=>"Book", :price=>15}, {:item=>"Pen", :price=>2}], 
#     "Bob"=>[{:item=>"Pen", :price=>3}]}

Collecting Multiple Results at Once

data = [1, 2, 3, 4, 5]
results = data.each_with_object({ positives: [], negatives: [], zeros: [] }) do |n, hash|
  if n > 0
    hash[:positives] << n
  elsif n < 0
    hash[:negatives] << n
  else
    hash[:zeros] << n
  end
end
# => {positives:[1, 2, 3, 4, 5], negatives:[], zeros:[]}

Flattening Nested Structures

nested = [[1, 2], [3, 4], [5, 6]]
flat = nested.each_with_object([]) { |subarr, arr| arr.concat(subarr) }
# => [1, 2, 3, 4, 5, 6]

With Hashes

stock = { apples: 5, oranges: 3, bananas: 0 }
out_of_stock = stock.each_with_object([]) { |(k, v), arr| arr << k if v.zero? }
# => [:bananas]

# Transform a hash into another hash
prices = { apple: 1, banana: 2, cherry: 5 }
doubled = prices.each_with_object({}) { |(k, v), h| h[k] = v * 2 }
# => {:apple=>2, :banana=>4, :cherry=>10}

Common Mistakes

Forgetting the Return Value

Unlike inject, the block return value does not matter:

# This works fine, even though block returns nil
[1, 2, 3].each_with_object([]) { |n, arr| arr << n if n.even? }
# => [2]

# The accumulator object is returned, not the block result

Confusing Parameter Order

Remember: element comes first, then the accumulator:

# Wrong - this is a common mistake
[1, 2, 3].each_with_object([]) { |arr, n| arr << n }
# => NoMethodError: undefined method `<<' for nil:NilClass

# Correct - element, then accumulator
[1, 2, 3].each_with_object([]) { |n, arr| arr << n }
# => [1, 2, 3]

Performance Notes

  • each_with_object creates your accumulator object once, then modifies it
  • Avoid creating new objects inside the block if performance matters
  • For simple numeric accumulation, inject/reduce is often clearer

Return Value

Always returns the object you passed in, regardless of what happens in the block:

result = [1, 2, 3].each_with_object({}) { |n, h| h[n] = n * 2 }
result  # => {1=>2, 2=>4, 3=>6}
# The exact same object you passed as the second argument

See Also