rubyguides

Array#reduce

reduce(initial = nil) { |memo, element| block } -> result

reduce is an alias for inject. It iterates over an array while maintaining an accumulator value. On each iteration, the block receives the current accumulator (called memo) and the current element. The block return value becomes the new accumulator. After processing all elements, reduce returns the final accumulator value.

Syntax

array.reduce(initial) { |memo, element| block }
array.reduce { |memo, element| block }

Parameters

ParameterTypeDefaultDescription
initialObjectnilStarting value for the accumulator. If omitted, the first element becomes the initial accumulator.

The two syntax forms differ in what happens when the array is empty and in what type the accumulator starts with. Passing an explicit initial value gives you control over both: the block always runs with initial as the first memo, and an empty array returns initial directly instead of nil. Skipping the initial value is fine for non-empty arrays where the first element is a natural starting point.

Examples

Summing numbers

numbers = [1, 2, 3, 4, 5]

sum = numbers.reduce(0) { |memo, n| memo + n }
puts sum  # => 15

sum = numbers.reduce { |memo, n| memo + n }
puts sum  # => 15

The reducer block works the same way whether you provide an explicit initial value or let the first element serve that role, so both forms produce identical results here. In practice, providing the initial value makes the code’s intent clearer and avoids surprises when the array turns out to be empty later on.

Building a hash

items = [:a, :b, :c]

indexed = items.reduce({}) do |memo, item|
  memo[item] = item.to_s.upcase
  memo
end
puts indexed  # => {:a=>"A", :b=>"B", :c=>"C"}

Building a hash with reduce shows how the accumulator can be a mutable collection that grows with each iteration, as long as the block returns the updated object. This pattern is common when transforming lists into lookup tables — the block does some work and then hands back the memo for the next round.

Using symbol shorthand

# Concatenate strings
words = ["Hello", "World", "!"].reduce(:+)
puts words  # => "HelloWorld!"

# Multiply all elements
product = [2, 3, 4, 5].reduce(:*)
puts product  # => 120

The symbol shorthand reduce(:+) is a concise alternative when the operation is a method that takes two arguments and returns a result — Ruby calls memo.send(:+, element) on each iteration. This works for :+, :*, :-, :/, and any other binary method, making it a handy shortcut for common reductions.

Common Patterns

# Start from different initial value
result = [1, 2, 3].reduce(10) { |memo, n| memo + n }
puts result  # => 16

# Chaining
result = [1, 2, 3, 4, 5, 6]
  .select(&:even?)
  .map { |n| n * 2 }
  .reduce(0) { |memo, n| memo + n }
puts result  # => 24

Chaining reduce after select and map is a common pattern in data processing pipelines: filter to the elements you need, transform them, then fold the result into a single value. Because reduce returns whatever the last block evaluation produces, it fits naturally at the end of a chain that builds toward a summary.

Errors

# Empty without initial returns nil
[].reduce { |m, e| m + e }  # => nil

# Empty with initial returns initial
[].reduce(0) { |m, e| m + e }  # => 0

keeping reducers readable

reduce is powerful because it lets one accumulator step summarize an entire collection, but the block still needs to stay easy to read. A reducer works best when the change from one iteration to the next is obvious, such as a sum, a merge, or a small transformation. If the block starts to contain several branches or side effects, the intent gets harder to follow and a named helper can be clearer. Keeping the accumulator shape simple makes the method easy to test and easy to trust.

It also helps to choose a meaningful initial value when the collection may be empty or when the output should always have a stable type. That keeps the behavior predictable and avoids edge cases where the first element suddenly becomes the accumulator. In data processing code, this small choice often makes the whole pipeline feel steadier.

choosing a clear starting value

The initial value does more than fill a gap for empty arrays. It also tells the reader what kind of result the reducer is building and how the first step should behave. A good seed value makes the block easier to reason about because the accumulator always starts from something known instead of borrowing the first element and changing its role. That makes totals, merges, and summary transforms feel more deliberate and keeps later code from guessing at the output shape.

See Also