ruby.onl / dark-arts

inject/reduce: Fold Your Data Into Anything

2026-03-24

inject (aliased as reduce) accumulates a value across a collection. It's like Perl's List::Util::reduce, but with a cleaner shorthand syntax. Sum numbers, build hashes, concatenate strings, merge objects. If you can describe it as "start with X, then do Y for each element," inject handles it.

Part 1: Symbol Form (The Clean Way)

The cleanest form, just pass the operator as a symbol:
# Sum (1..10).inject(:+) # => 55 # Product (1..10).inject(:*) # => 3628800 # With initial value (1..5).inject(100, :+) # => 115 (100 + 1 + 2 + 3 + 4 + 5) # String concatenation %w~hello world~.inject(:+) # => "helloworld"
No & needed here. inject accepts a bare symbol directly, unlike map and friends.

Part 2: Block Form

For custom accumulation logic:
# Sum with block (equivalent to inject(:+)) (1..10).inject { |sum, n| sum + n } # Build a hash from an array words.inject(Hash.new(0)) do |counts, word| counts[word] += 1 counts # must return accumulator! end # Find longest string words.inject do |longest, word| word.length > longest.length ? word : longest end

Part 3: The Accumulator Pattern

The block receives two arguments:
  1. The accumulator (running total / result being built)
  2. The current element

Whatever the block returns becomes the next accumulator value. Forget to return the accumulator and you'll get weird results. This is the number one inject bug.

# Building a string lines.inject("") do |result, line| result + line.chomp + "\n" end # With initial value lines.inject("HEADER\n") do |result, line| result + " #{line.chomp}\n" end

Part 4: Perl Comparison

# Perl (with List::Util): use List::Util qw(reduce); my $sum = reduce { $a + $b } @nums;
# Ruby: sum = nums.inject(:+)
One line. One symbol. Done.

Part 5: each_with_object (The Better Alternative)

When building a collection, each_with_object is often cleaner because you don't need to return the accumulator:
# inject (must return accumulator): words.inject(Hash.new(0)) do |counts, word| counts[word] += 1 counts # easy to forget this! end # each_with_object (accumulator is auto-returned): words.each_with_object(Hash.new(0)) do |word, counts| counts[word] += 1 # no return needed! end
Note the argument order is reversed: each_with_object puts the element first, object second. Annoying, but worth it for not having to remember to return the accumulator.

Part 6: Practical Examples

# Merge multiple hashes hashes = [{a: 1}, {b: 2}, {c: 3}] hashes.inject(:merge) # => {a: 1, b: 2, c: 3} # Build comma-separated string with oxford comma items = %w~apples oranges bananas~ items[0..-2].join(", ") + ", and " + items[-1] # => "apples, oranges, and bananas"
Use inject(:+) for sums, inject(:merge) for hash merging, inject(:*) for products. Use each_with_object when building hashes or arrays. Use the block form when your logic doesn't fit neatly into a single operator.

Created By: Wildcard Wizard. Copyright 2026