Aggregate Operations
An aggregate operation turns a whole collection into one value. Examples are the number of elements, their sum, the biggest element, or all elements combined into one result.
PHP gives you the common ones as built-in functions. count() counts the elements, min() and max() find the smallest and the biggest element, and array_sum() adds them up.
Be careful: min() and max() throw a ValueError on an empty array ("must contain at least one element"), and the program stops unless you catch it. Check count($s) > 0 first when the array might be empty. array_sum([]) is simply 0.
For combining elements one by one there is array_reduce($items, $callback, $initial). It keeps a running value, the accumulator, and calls your function with the accumulator and each element in turn. The accumulator starts as $initial — and when you leave $initial out, it starts as null, not as the first element. This page shows what that means and how to get both styles of combining, often called fold and reduce.
Basic Aggregates
The most common aggregates are count, minimum, maximum, sum and average.
count($numbers)gives the count.min($numbers)andmax($numbers)give the smallest and the biggest element. They work for numbers and strings.array_sum($numbers)adds every element up. The average is the sum divided by the count. PHP's/keeps the decimal part when the division does not come out even, so 62 / 4 is 15.5.
Sometimes the smallest or biggest element should be picked by a rule, not by the value itself. min() and max() take no rule, but array_reduce() can keep the better element: at each step, compare the best element so far with the current one and keep the winner.
Sometimes every element must be converted with a function first and the results added up. Chain two built-ins: array_map() converts, array_sum() adds.
Count, min, max, sum and average
For the numbers 6, 42, 10 and 4 print the count with count(), the biggest and smallest element with max() and min(), and the sum with array_sum(). The average is 62 / 4 = 15.5.
Output: PHP script
<?php
$numbers = [6, 42, 10, 4];
$sum = array_sum($numbers);
echo "Count: ", count($numbers), "\n";
echo "Max: ", max($numbers), "\n";
echo "Min: ", min($numbers), "\n";
echo "Average: ", $sum / count($numbers), "\n";
echo "Sum: ", $sum, "\n";
Min and max by a rule
The first array_reduce() keeps the element with the smaller remainder % 3. The remainders of 5, 42, 10 and 4 are 2, 0, 1 and 1, so 42 wins. The second keeps the longer string and finds "three". No initial value is given, so $best starts as null, and the $best === null check lets the first element win the first round. On a tie the earlier element stays.
Output: PHP script
<?php
$numbers = [5, 42, 10, 4];
$words = ["one", "two", "three", "four"];
// The element whose remainder after dividing by 3 is the smallest.
// $best starts as null, so the first element always wins the first round.
$minRem = array_reduce(
$numbers,
fn($best, $n) => ($best === null || $n % 3 < $best % 3) ? $n : $best
);
echo $minRem, "\n";
// The longest word, compared by length.
$longest = array_reduce(
$words,
fn($best, $w) => ($best === null || strlen($w) > strlen($best)) ? $w : $best
);
echo $longest, "\n";
Sum of a function of each element
array_map() converts every element, array_sum() adds the results. Doubling 5, 42, 10 and 4 gives 122. Halving them gives 30.5.
Output: PHP script
<?php
$numbers = [5, 42, 10, 4];
// Convert every element first, then add the results up.
echo array_sum(array_map(fn($n) => $n * 2, $numbers)), "\n";
echo array_sum(array_map(fn($n) => $n / 2, $numbers)), "\n";
Fold and Reduce
Fold and reduce combine all elements into one value, step by step. They keep a running value called the accumulator. For each element they call a function with the accumulator and the element, and the function returns the new accumulator.
The difference is the starting value:
- A fold starts with an initial value that you give it. The function runs once for every element. In PHP this is exactly
array_reduce($items, $f, $initial). - A reduce starts with the first element. The function runs for the second element onward, so the first element is never passed through it.
Despite its name, array_reduce() is always a fold. Without a third argument it starts with null, and in arithmetic null counts as 0 — so the function still runs for every element. A real reduce takes the first element as the start and folds the rest: array_reduce(array_slice($items, 1), $f, $items[0]). A small reduce() helper wraps that line.
This matters when the function changes the element. If it doubles every element, a reduce leaves the first element as it is.
PHP has no reduce-from-the-right. To fold from the right, hand array_reduce() the reversed array from array_reverse(): the elements arrive last to first, and the function still gets the accumulator first and the element second.
Reduce
The reduce() helper takes 5 as the start and folds 2, 10 and 4 into it. The result is 21.
Output: PHP script
<?php
// reduce starts with the first element and folds the rest into it.
function reduce(array $items, callable $f): mixed {
return array_reduce(array_slice($items, 1), $f, $items[0]);
}
$numbers = [5, 2, 10, 4];
$sum = reduce($numbers, fn($sum, $element) => $sum + $element);
echo $sum, "\n";
Fold
array_reduce() with the initial value 0 starts at 0 and adds each element times 2. Every element is doubled: 10 + 4 + 20 + 8 = 42.
Output: PHP script
<?php
$numbers = [5, 2, 10, 4];
// The third argument is the initial value: every element is doubled.
$sumDoubled = array_reduce($numbers, fn($sum, $element) => $sum + $element * 2, 0);
echo $sumDoubled, "\n";
Fold versus Reduce
Both calls use the same function, accumulator plus element times 2. reduce() gives 37, because the first element 5 is the start and is not doubled. array_reduce() with the initial value 0 gives 42. Leaving out the 0 would also give 42: the start would be null, and null + 10 is 10.
Output: PHP script
<?php
// reduce uses the first element as the start, so $f is never applied to it.
function reduce(array $items, callable $f): mixed {
return array_reduce(array_slice($items, 1), $f, $items[0]);
}
$addDoubled = fn($sum, $element) => $sum + $element * 2;
$numbers = [5, 2, 10, 4];
// 5 is the starting value here, so it is not doubled: 5 + 4 + 20 + 8
echo "Reduce: ", reduce($numbers, $addDoubled), "\n";
// Every element is doubled: 10 + 4 + 20 + 8
echo "Fold: ", array_reduce($numbers, $addDoubled, 0), "\n";
Fold from the right
array_reverse($numbers) hands the elements to array_reduce() from the last one to the first. Addition does not care about the order, so the result is again 42.
Output: PHP script
<?php
$numbers = [5, 2, 10, 4];
// array_reverse() hands the elements over from the last one to the first.
$sumDoubledRight = array_reduce(
array_reverse($numbers),
fn($sum, $element) => $sum + $element * 2,
0
);
echo $sumDoubledRight, "\n";
Fold with Index
Sometimes the position of an element matters, not only its value.
The callback of array_reduce() never sees the keys, but a plain foreach loop does. foreach ($numbers as $idx => $element) walks from the start. array_reverse($numbers, true) reverses the array but keeps the original keys, because the second argument is true — so a foreach over it walks from the end and still sees each element's real index. Inside the loop an if checks the index and decides whether to add the element.
Fold with index
Only the elements at even indexes are added: index 0 holds 5 and index 2 holds 10, so the sum is 15.
Output: PHP script
<?php
$numbers = [5, 2, 10, 4];
// foreach gives the index too, so the condition can use it.
$sumEven = 0;
foreach ($numbers as $idx => $element) {
if ($idx % 2 === 0) {
$sumEven += $element;
}
}
echo $sumEven, "\n";
Fold with index from the right
The same rule, but the loop starts at index 3 and goes down to 0. The result is still 15.
Output: PHP script
<?php
$numbers = [5, 2, 10, 4];
// array_reverse(..., true) keeps the original keys: 3, 2, 1, 0.
$sumEven = 0;
foreach (array_reverse($numbers, true) as $idx => $element) {
if ($idx % 2 === 0) {
$sumEven += $element;
}
}
echo $sumEven, "\n";
Safe Reduce
A reduce needs a first element to start with. On an empty array the reduce() helper would read $items[0], which does not exist: PHP prints a warning ("Undefined array key 0") and carries on with null.
The PHP way to say "there is no result" is null. A small helper checks the array first: when it is empty, it returns null without touching $items[0]. The caller then checks for null, just like $map[$key] ?? $default handles a missing key. array_reduce() itself already behaves this way: on an empty array it returns its initial value, which is null when you give none.
Reduce or null
reduceOrNull() returns 21 for the numbers 5, 2, 10 and 4, and null for an empty array. The last line shows that array_reduce() without an initial value also returns null for an empty array. json_encode() prints null as the word null.
Output: PHP script
<?php
// reduceOrNull returns null instead of reading a missing $items[0].
function reduceOrNull(array $items, callable $f): mixed {
if ($items === []) {
return null;
}
return array_reduce(array_slice($items, 1), $f, $items[0]);
}
$add = fn($sum, $element) => $sum + $element;
$numbers = [5, 2, 10, 4];
$empty = [];
echo json_encode(reduceOrNull($numbers, $add)), "\n";
echo json_encode(reduceOrNull($empty, $add)), "\n";
// array_reduce() on an empty array returns its initial value: null here.
echo json_encode(array_reduce($empty, $add)), "\n";
Running Fold and Reduce
A running fold or reduce does not throw away the steps in between. It saves the accumulator after every element, so the result is an array of all the running values.
PHP has no built-in function for this. Keep a sum variable and append it to a result array with $result[] = $sum inside a foreach loop. For a running reduce the result starts with the first element. For a running fold the result starts with the initial value and has one more element than the input.
Running reduce
The numbers 0 to 5 are added one by one. The result array holds every sum along the way: 0, 1, 3, 6, 10, 15.
Output: PHP script
<?php
$numbers = [0, 1, 2, 3, 4, 5];
// The first element is the start; every later step is saved in the result.
$sum = $numbers[0];
$runningReduceSum = [$sum];
foreach (array_slice($numbers, 1) as $item) {
$sum += $item;
$runningReduceSum[] = $sum;
}
echo implode(", ", $runningReduceSum), "\n";
Running fold
The same with the initial value 10. The result begins with 10 itself and has one more element than the input: 10, 10, 11, 13, 16, 20, 25.
Output: PHP script
<?php
$numbers = [0, 1, 2, 3, 4, 5];
// The initial value 10 is the first element of the result.
$sum = 10;
$runningFoldSum = [$sum];
foreach ($numbers as $item) {
$sum += $item;
$runningFoldSum[] = $sum;
}
echo implode(", ", $runningFoldSum), "\n";