inject/reduce: Fold Your Data Into Anything
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:No# 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"
& 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:- The accumulator (running total / result being built)
- 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;
One line. One symbol. Done.# Ruby: sum = nums.inject(:+)
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:
Note the argument order is reversed:# 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
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
Use# 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"
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