JSworkingevergreen

Array.reduce patterns

One loop, any shape of answer.

reduce folds an array into a single value of any shape β€” a number, an object, a map β€” by threading an accumulator through every element; learn the handful of patterns and you stop reaching for manual loops.

What reduce actually does

array.reduce(callback, initialValue) walks the array left to right, carrying an accumulator from one step to the next. Each call is callback(accumulator, item, index) and whatever it returns becomes the accumulator for the following element; the final accumulator is the result. The shape of the accumulator is entirely yours β€” a running total, an object you keep filling, a Map, even another array.

Always pass an explicit initialValue. Without it, reduce uses the first element as the seed and starts at index 1 β€” which throws on an empty array and quietly changes the accumulator's type when your fold returns something other than an element. Seeding with 0, {}, [], or new Map() makes the intended output shape obvious and handles the empty case for free.

The everyday folds

Most reduces are one of a few recurring shapes. Sum seeds with 0 and adds a field. indexBy seeds with {} and files each item under a unique key so you can look it up in O(1). group by is the same idea but appends to an array per key, using ??= to lazily create the bucket. flatten one level seeds with [] and spreads each inner array in. count occurrences seeds with {} and bumps a tally with ?? 0.

These all share a rhythm: pick the seed that matches your output, then mutate-and-return the accumulator each step. Returning the accumulator is mandatory β€” forget it and the next step receives undefined.

Reduce over functions: pipeline and compose

The accumulator does not have to be data. If the array holds functions, you can fold an input value through each one β€” that is a pipeline. pipe(...fns) reduces the functions left to right (fns.reduce((acc, fn) => fn(acc), input)); compose(...fns) does the same right to left with reduceRight. This turns a list of small transforms into one combined transform without nesting calls.

When NOT to reduce

reduce earns its place when you are collapsing to a new shape (an object, a map, a single scalar). For a plain bucketing job, Object.groupBy / Map.groupBy read more clearly than a hand-rolled group reduce. For find-the-largest, a reduce works but spelling the comparison out (max-by) can be clearer than chaining sort().at(-1), which sorts the whole array just to read one end. Reach for reduce when the alternative is a manual loop with an externally-declared accumulator.

Try it 6 examples

Sum and average a field

JSintro
Press Run to execute

The fold collapses four order objects into a single total: 180, then average: 45 derives from it β€” the length guard means an empty array yields 0 instead of NaN (dividing by zero) or a throw.