Signature
array_reduce(array $array, callable $callback, mixed $initial = null): mixedPurpose
array_reduce() walks through an array from the first element to the last and combines the elements into one result. That result can be a number, a string, an array, or any other value. The function keeps a running value, called the carry, and passes it with each element to your callback. Whatever the callback returns becomes the carry for the next step. Typical uses are totals, products, counts, longest-string searches, and building a new structure such as a grouped array.
Signature
The callback has this shape:
function callback(mixed $carry, mixed $item): mixed
Parameters and defaults
$arrayis the input array. Elements are visited in their stored order.$callbackreceives the carry and the current item, and must return the new carry.$initialis the starting carry. It defaults tonull. If the array is empty, this value is returned unchanged.
On the first iteration $carry holds $initial. If you leave it out, the first callback call receives null as the carry, not the first array element. This differs from reduce functions in some other languages.
Return value and failure cases
The function returns the final carry. For an empty array it returns $initial, which is null when you did not supply one. It does not throw for an empty array, so a missing result is easy to overlook. Passing something that is not an array or not callable raises a TypeError. Since PHP 8.0.0, a callback that expects a by-reference parameter triggers an E_WARNING, so write the callback to take plain values and return the new carry.
Example 1: sums and products
The first example reduces the same list twice. The sum starts at 0, and the product starts at 10, so the product is 10 * 1 * 2 * 3 * 4 * 5.
$numbers = [1, 2, 3, 4, 5];
$sum = array_reduce($numbers, fn($carry, $item) => $carry + $item, 0);
$product = array_reduce($numbers, fn($carry, $item) => $carry * $item, 10);
echo json_encode(['sum' => $sum, 'product' => $product]), "\n";{"sum":15,"product":1200}
Example 2: the empty array
When there is nothing to iterate, the callback never runs. You get back exactly what you passed as the initial value, or null if you passed nothing.
$sum = fn($carry, $item) => $carry + $item;
var_dump(array_reduce([], $sum));
var_dump(array_reduce([], $sum, 'No data to reduce'));
var_dump(array_reduce([], $sum, 0));NULL string(17) "No data to reduce" int(0)
Example 3: building an array
The carry does not have to be a number. Here it is an array that grows as each word is filed under its first letter. Starting from [] keeps the result an array even when the input is empty.
$words = ['apple', 'avocado', 'banana', 'blueberry', 'cherry'];
$grouped = array_reduce($words, function (array $carry, string $word): array {
$letter = $word[0];
$carry[$letter][] = $word;
return $carry;
}, []);
echo json_encode($grouped), "\n";{"a":["apple","avocado"],"b":["banana","blueberry"],"c":["cherry"]}
Edge cases
- No initial value: the first call gets
nullas$carry. Arithmetic withnullworks like0, which hides the problem for sums but breaks product calculations, sincenull * 3is0. Always pass1as the initial value for products. - Keys are not passed: the callback sees only values. If you need keys, reduce over
array_keys()and look up each value, or use aforeachloop. - Order matters: for non-commutative operations such as string concatenation or subtraction, the order of the array decides the result. There is no right-to-left variant, so reverse the array first with
array_reverse()if you need one. - Type of the initial value: it decides the type of the empty-array result, so choose one that matches what the callback returns.
- Typed callbacks: if you declare
array $carryand forget the initial value, the first call receivesnulland throws aTypeError.
Common mistakes
- Forgetting to return the carry. A callback without
returnhandsnullto the next iteration, and the final result is usuallynullor wrong. Arrow functions return their expression automatically, which avoids this. - Mutating outside state. Using
use (&$total)inside the callback defeats the purpose. Put the accumulated state in the carry instead. - Using it where a simpler function fits. For plain totals,
array_sum()is clearer, andarray_product()handles products. Usearray_reduce()when the combining logic is custom. - Treating a
nullresult as a value. An empty input without an initial value givesnull, so always provide an initial value when the array might be empty. - Confusing it with
array_map().array_map()produces an array of the same length.array_reduce()collapses the array to a single result.
Related functions
array_map()transforms each element and keeps one output per input.array_filter()keeps only the elements that pass a test.array_sum()andarray_product()cover the two most common reductions.array_count_values()counts occurrences of each distinct value.array_unique()removes duplicate values.
Recap
array_reduce() folds an array into one value by calling your callback with a carry and each item. Pass an explicit initial value so that the first iteration and the empty-array case behave predictably. Make sure the callback always returns the new carry. The callback sees values only, and elements are processed in array order.
Tested examples
$numbers = [1, 2, 3, 4, 5];
$sum = array_reduce($numbers, fn($carry, $item) => $carry + $item, 0);
$product = array_reduce($numbers, fn($carry, $item) => $carry * $item, 10);
echo json_encode(['sum' => $sum, 'product' => $product]), "\n";{"sum":15,"product":1200}
$sum = fn($carry, $item) => $carry + $item;
var_dump(array_reduce([], $sum));
var_dump(array_reduce([], $sum, 'No data to reduce'));
var_dump(array_reduce([], $sum, 0));NULL string(17) "No data to reduce" int(0)
$words = ['apple', 'avocado', 'banana', 'blueberry', 'cherry'];
$grouped = array_reduce($words, function (array $carry, string $word): array {
$letter = $word[0];
$carry[$letter][] = $word;
return $carry;
}, []);
echo json_encode($grouped), "\n";{"a":["apple","avocado"],"b":["banana","blueberry"],"c":["cherry"]}