Collection::reduce

Folds a collection down to one value by threading a carry through every item.

Signature
Collection::reduce(callable $callback, mixed $initial = null): mixed

Parameters

ParameterTypeDescription
$$callbackcallableInvoked as ($carry, $value, $key) for every item; its return value becomes the $carry for the next item.
$$initialoptionalmixed= nullThe starting value of the carry. Left at null, the first iteration receives null.

Return value

mixedThe value returned by the final invocation of the callback, or $initial if the collection is empty.

The argument order is the thing people get wrong. Every other collection callback starts with the item — map(fn ($item, $key) => ...) — but here the accumulator comes first: fn ($carry, $item, $key). Swapping them produces silently wrong results rather than an error, because both arguments are usually the same type.

The second gotcha is the default carry. Without an $initial, the first call receives null, so a summing callback must accept ?int $carry and rely on null + 1. Under strict types, or when you are concatenating strings or pushing into an array, that null becomes a TypeError. Always pass an explicit initial value — 0, '', [] — unless you deliberately want the null.

Reach for it when the result is a different shape from the input and no dedicated method exists: totalling line items with per-row tax, collapsing a list of ranges into one, or building a lookup array. If you are only summing a column, sum() is clearer; if the result is another collection, map() or filter() reads better. Forgetting to return from the callback silently sets the carry to null.

reduceSpread() is the sibling that carries several accumulators at once.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Total an order's line items where each row applies its own tax or discount rate.
  • Convert a list of currency amounts to one base-currency figure using per-key rates.
  • Collapse a collection into a single associative lookup array keyed by id.

Frequently asked questions

Which argument comes first in a reduce callback?

The carry comes first: fn ($carry, $item, $key). That is the reverse of map(), filter() and each(), which all start with the item, and mixing them up produces wrong totals rather than an error.

Why is my carry null on the first pass?

Because $initial defaults to null when you do not pass a second argument. Pass 0, '' or [] explicitly, or type the carry as nullable, otherwise strict types will throw a TypeError on the first iteration.

When should I use reduce instead of sum or map?

Use reduce when the result has a different shape from the input or when each item's contribution depends on other data. A plain column total is clearer with sum(), and a one-to-one transformation belongs in map().

More examples

Using the key as well as the value

The third parameter is the array key, which lets the callback look up a per-key rate. Output is 67500 then 675.00 — 2x20000 plus 4x5000 plus 3x2500. Working in integer cents keeps the total exact; the same reduce over floats would drift.

Example · php
<?php

$stock = collect([
    'desk' => 2,
    'chair' => 4,
    'lamp' => 3,
]);

$unitPriceInCents = [
    'desk' => 20000,
    'chair' => 5000,
    'lamp' => 2500,
];

$inventoryValue = $stock->reduce(
    fn (int $carry, int $qty, string $sku) => $carry + ($qty * $unitPriceInCents[$sku]),
    0
);

echo $inventoryValue, PHP_EOL;
echo number_format($inventoryValue / 100, 2), PHP_EOL;

Building a structure instead of a number

The carry starts as [] and each pass adds one entry, producing [1 => Ada, 2 => Linus, 3 => Grace] after the ksort. Note the explicit return — a closure that mutates $carry but forgets to return it leaves the carry null on the next iteration.

Example · php
<?php

$rows = collect([
    ['id' => 3, 'name' => 'Grace'],
    ['id' => 1, 'name' => 'Ada'],
    ['id' => 2, 'name' => 'Linus'],
]);

$byId = $rows->reduce(function (array $carry, array $row) {
    $carry[$row['id']] = $row['name'];

    return $carry;
}, []);

ksort($byId);
print_r($byId);

Two accumulators with reduceSpread

reduceSpread carries several values at once: the callback receives one argument per accumulator followed by the item, and must return an array of the same length. Output is count=4 sum=43 mean=10.75. Returning a differently sized array throws UnexpectedValueException.

Example · php
<?php

[$count, $sum] = collect([4, 8, 15, 16])->reduceSpread(
    fn (int $count, int $sum, int $item) => [$count + 1, $sum + $item],
    0,
    0
);

echo "count={$count} sum={$sum} mean=" . ($sum / $count), PHP_EOL;

Discussion

  • Be the first to comment on this lesson.