Himanshu SharmaSoftware engineer
Let’s talk

PHP · Lesson 27 of 54

array_map, array_filter and array_reduce

Transform every element with array_map, keep matching elements with array_filter, and combine an array into one value with array_reduce.

12 minArraysUpdated 22 October 2026

Awaiting technical review. This lesson is readable but not yet listed in search engines.

By the end of this lessonReplace common loops with array_map, array_filter and array_reduce, and chain them into a small data pipeline.

Three patterns behind most loops

Look at the loops you have written so far and most of them do one of three things:

  1. Transform every element (add tax to every price).
  2. Keep some elements (only products in stock).
  3. Combine all elements into one value (the cart total).

PHP has a function for each: array_map(), array_filter() and array_reduce(). With arrow functions, each becomes one short, readable line, and none of them changes the original array.

array_map: transform every element

array_map() calls a function on every element and returns a new array of the results. The callback comes first:

php
<?php
$prices = [89.5, 18.75, 24.99];

$withTax = array_map(fn($price) => round($price * 1.18, 2), $prices);
print_r($withTax);

$names = ["mechanical keyboard", "ceramic mug set"];
print_r(array_map('ucwords', $names));
result
Array
(
    [0] => 105.61
    [1] => 22.13
    [2] => 29.49
)
Array
(
    [0] => Mechanical Keyboard
    [1] => Ceramic Mug Set
)

Compare that with the loop it replaces: create an empty array, loop, calculate, append. array_map() says "the same list, transformed" in one line.

With rows, array_map() can reshape each row, for example to prepare data for display or for JSON:

php
<?php
$products = [
    ["name" => "Yoga Mat", "price" => 24.99, "stock" => 80],
    ["name" => "Puzzle 1000 Pieces", "price" => 19.99, "stock" => 0],
];

$labels = array_map(
    fn($p) => $p["name"] . ($p["stock"] > 0 ? "" : " (sold out)"),
    $products
);
print_r($labels);
result
Array
(
    [0] => Yoga Mat
    [1] => Puzzle 1000 Pieces (sold out)
)

array_map() keeps string keys when you pass a single array. Passing several arrays walks them side by side: array_map(fn($a, $b) => $a * $b, $prices, $quantities) multiplies matching elements.

array_filter: keep some elements

array_filter() keeps the elements for which the callback returns true. The array comes first here:

php
<?php
$products = [
    ["name" => "Mechanical Keyboard", "price" => 89.5, "stock" => 60],
    ["name" => "Puzzle 1000 Pieces", "price" => 19.99, "stock" => 0],
    ["name" => "Headphones", "price" => 199.99, "stock" => 25],
    ["name" => "Water Bottle", "price" => 12.0, "stock" => 150],
];

$inStock = array_filter($products, fn($p) => $p["stock"] > 0);
$cheap = array_filter($products, fn($p) => $p["price"] < 50);

echo implode(", ", array_column($inStock, "name")) . "\n";
echo implode(", ", array_column($cheap, "name")) . "\n";
print_r(array_keys($cheap));
result
Mechanical Keyboard, Headphones, Water Bottle
Puzzle 1000 Pieces, Water Bottle
Array
(
    [0] => 1
    [1] => 3
)

The last line shows an important detail: array_filter() keeps the original keys, so the cheap products are still at keys 1 and 3. Wrap the result in array_values() when you need a fresh list numbered from 0, for example before encoding it as a JSON array.

Without a callback, array_filter() removes every falsy value. That is a quick way to drop empty strings from a list, but remember that it also drops 0 and "0":

php
<?php
$fields = ["Asha", "", "Delhi", null, "0", "India"];
print_r(array_values(array_filter($fields)));
print_r(array_values(array_filter($fields, fn($v) => $v !== null && $v !== "")));
result
Array
(
    [0] => Asha
    [1] => Delhi
    [2] => India
)
Array
(
    [0] => Asha
    [1] => Delhi
    [2] => 0
    [3] => India
)

To filter by key as well, pass ARRAY_FILTER_USE_KEY or ARRAY_FILTER_USE_BOTH as the third argument.

array_reduce: combine into one value

array_reduce() walks the array and keeps an accumulator, a running result. The callback receives the accumulator so far and the current element, and returns the new accumulator. The third argument is the starting value:

php
<?php
$cart = [
    ["product" => "Mechanical Keyboard", "qty" => 1, "price" => 89.5],
    ["product" => "Ceramic Mug Set", "qty" => 2, "price" => 18.75],
    ["product" => "Water Bottle", "qty" => 3, "price" => 12.0],
];

$total = array_reduce($cart, fn($sum, $line) => $sum + $line["qty"] * $line["price"], 0);
$items = array_reduce($cart, fn($count, $line) => $count + $line["qty"], 0);

echo "Total: $total\n";
echo "Items: $items\n";
result
Total: 163
Items: 6

Step by step for the total: the accumulator starts at 0, becomes 89.5 after the keyboard, 127 after the mugs and 163 after the bottles. The accumulator does not have to be a number. It can be a string, or an array, for example to group or index rows:

php
<?php
$cart = [
    ["product" => "Keyboard", "qty" => 1],
    ["product" => "Mug", "qty" => 2],
];

$lookup = array_reduce($cart, function (array $carry, array $line): array {
    $carry[$line["product"]] = $line["qty"];
    return $carry;
}, []);

print_r($lookup);
result
Array
(
    [Keyboard] => 1
    [Mug] => 2
)

For a plain sum, array_sum(array_column(...)) is shorter; array_reduce() shines when the combining step is more than addition.

Chaining them

The three functions combine into small pipelines: filter, then map, then reduce. Read this from the inside out: keep in-stock products, work out each one's stock value, then add them up:

php
<?php
$products = [
    ["name" => "Mechanical Keyboard", "price" => 89.5, "stock" => 60],
    ["name" => "Puzzle 1000 Pieces", "price" => 19.99, "stock" => 0],
    ["name" => "Headphones", "price" => 199.99, "stock" => 25],
];

$stockValue = array_sum(
    array_map(
        fn($p) => $p["price"] * $p["stock"],
        array_filter($products, fn($p) => $p["stock"] > 0)
    )
);

echo "Value of stock on hand: $stockValue\n";
result
Value of stock on hand: 10369.75

If a chain becomes hard to read, break it into named steps with variables. A clear foreach is also perfectly good PHP; use these functions when they make the intention plainer.

Common mistakes

  • Mixing up the argument order: array_map($fn, $array) but array_filter($array, $fn).
  • Forgetting that array_filter() keeps keys, then getting a JSON object instead of a list.
  • Leaving out array_reduce()'s initial value, so the accumulator starts as null.
  • Filtering with no callback when 0 or "0" are valid values.

Exercise

From the cart below, produce the names of the lines whose line total is over 30, in capitals, as one comma-separated string. Expected: MECHANICAL KEYBOARD, WATER BOTTLE, CERAMIC MUG SET.

php
$cart = [
    ["product" => "Mechanical Keyboard", "qty" => 1, "price" => 89.5],
    ["product" => "Resistance Bands", "qty" => 1, "price" => 14.99],
    ["product" => "Water Bottle", "qty" => 3, "price" => 12.0],
    ["product" => "Ceramic Mug Set", "qty" => 2, "price" => 18.75],
];
Show one possible answer
php
<?php
$cart = [
    ["product" => "Mechanical Keyboard", "qty" => 1, "price" => 89.5],
    ["product" => "Resistance Bands", "qty" => 1, "price" => 14.99],
    ["product" => "Water Bottle", "qty" => 3, "price" => 12.0],
    ["product" => "Ceramic Mug Set", "qty" => 2, "price" => 18.75],
];

$big = array_filter($cart, fn($line) => $line["qty"] * $line["price"] > 30);
$names = array_map(fn($line) => strtoupper($line["product"]), $big);

echo implode(", ", $names) . "\n";
result
MECHANICAL KEYBOARD, WATER BOTTLE, CERAMIC MUG SET

Key points

  • array_map($callback, $array) returns a new array with the callback applied to every element.
  • array_filter($array, $callback) keeps the elements for which the callback returns true, and keeps their keys.
  • array_reduce($array, $callback, $initial) folds all elements into a single value such as a total.
  • Note the argument order: array_map takes the callback first, array_filter and array_reduce take the array first.

Check yourself

1. What does array_map(fn($x) => $x * 2, [1, 2, 3]) return?
2. What does array_filter([0, 1, 2, '', 'a']) return without a callback?
3. What is the third argument of array_reduce for?
4. Which one of these does NOT change the original array?