array_map / array_filter / array_reduce
array_map(), array_filter(), and array_reduce() are the three core functions for processing arrays without writing loops by hand. Each one expresses a common pattern:
| Function | Pattern | Input → Output |
|---|---|---|
array_map() | Transform every element | N elements → N elements |
array_filter() | Select elements that pass a test | N elements → 0…N elements |
array_reduce() | Combine all elements into one value | N elements → 1 value |
Each takes a callback — a function you provide (often an arrow function) that describes what to do with each element. The loop itself is handled by PHP.
$withGst = array_map(fn($price) => $price * 1.18, $prices); // transform
$cheap = array_filter($prices, fn($price) => $price < 500); // select
$total = array_reduce($prices, fn($sum, $price) => $sum + $price, 0); // combine
Why Use Them
- Intent is explicit: "map", "filter", and "reduce" describe what happens, while a
foreachloop must be read line by line to understand its purpose. - No temporary variables or accidental mutations: each function returns a new value and leaves the input array unchanged.
- Composable: the result of one can be passed directly to the next, forming a data pipeline.
array_map() — Transform Every Element
Syntax
array_map(?callable $callback, array $array, array ...$arrays): array
Example
<?php
$prices = [100, 250, 499];
$withGst = array_map(fn(float $price): float => round($price * 1.18, 2), $prices);
$labels = array_map(fn(float $price): string => '₹' . number_format($price, 2), $withGst);
echo implode(', ', $labels) . "\n";
echo implode(', ', $prices) . "\n";
Output
₹118.00, ₹295.00, ₹588.82
100, 250, 499
The callback receives each value and returns the new value. The result has the same number of elements, and the original $prices is unchanged.
Keys Are Preserved With One Array
<?php
$prices = ['Pen' => 20, 'Lamp' => 1299];
print_r(array_map(fn(int $price): int => $price * 2, $prices));
Output
Array
(
[Pen] => 40
[Lamp] => 2598
)
With a single input array, array_map() keeps the keys. With several arrays, the result is re-indexed.
Accessing Keys in the Callback
The callback receives only values. To use the keys too, pass array_keys() as a second array:
<?php
$prices = ['Pen' => 20, 'Lamp' => 1299];
$lines = array_map(
fn(string $item, int $price): string => "$item: ₹$price",
array_keys($prices),
$prices
);
print_r($lines);
Output
Array
(
[0] => Pen: ₹20
[1] => Lamp: ₹1299
)
Combining Several Arrays
When given multiple arrays, the callback receives one element from each, position by position:
<?php
$names = ['Asha', 'Bala', 'Charu'];
$scores = [88, 72, 95];
print_r(array_map(fn(string $name, int $score): string => "$name scored $score", $names, $scores));
echo json_encode(array_map(null, [1, 2], ['a', 'b'])) . "\n";
Output
Array
(
[0] => Asha scored 88
[1] => Bala scored 72
[2] => Charu scored 95
)
[[1,"a"],[2,"b"]]
Passing null as the callback "zips" the arrays into pairs. If the arrays differ in length, shorter ones are padded with null.
array_filter() — Keep Matching Elements
Syntax
array_filter(array $array, ?callable $callback = null, int $mode = 0): array
Example
<?php
$scores = ['Asha' => 88, 'Bala' => 34, 'Charu' => 95, 'Dev' => 39];
$passed = array_filter($scores, fn(int $score): bool => $score >= 40);
print_r($passed);
Output
Array
(
[Asha] => 88
[Charu] => 95
)
The callback returns true to keep an element. Keys are preserved — wrap the result in array_values() if you need a list with continuous indexes (for example, before json_encode()).
Filtering by Key or by Key and Value
<?php
$env = ['DB_HOST' => 'localhost', 'DB_PORT' => '3306', 'MAIL_DRIVER' => 'smtp', 'DB_NAME' => 'shop'];
$dbSettings = array_filter(
$env,
fn(string $key): bool => str_starts_with($key, 'DB_'),
ARRAY_FILTER_USE_KEY
);
$stock = ['Pen' => 100, 'Lamp' => 0, 'Bag' => 4, 'Mug' => 12];
$available = array_filter(
$stock,
fn(int $qty, string $item): bool => $qty > 0 && $item !== 'Mug',
ARRAY_FILTER_USE_BOTH
);
echo implode(', ', array_keys($dbSettings)) . "\n";
echo implode(', ', array_keys($available)) . "\n";
Output
DB_HOST, DB_PORT, DB_NAME
Pen, Bag
| Mode | Callback Receives |
|---|---|
0 (default) | The value |
ARRAY_FILTER_USE_KEY | The key |
ARRAY_FILTER_USE_BOTH | The value and the key |
Without a Callback
Without a callback, array_filter() removes all falsy values — false, null, 0, 0.0, '', '0', and []. This is convenient for removing empty entries but also removes valid zeros. Pass an explicit callback for user data (see Remove Array Items).
array_reduce() — Combine Into One Value
Syntax
array_reduce(array $array, callable $callback, mixed $initial = null): mixed
The callback receives an accumulator (often called $carry) and the current element. Whatever it returns becomes the accumulator for the next element. After the last element, the accumulator is the result.
Example: Sum
<?php
$cart = [499, 1299, 89];
$total = array_reduce($cart, fn(int $carry, int $price): int => $carry + $price, 0);
echo $total . "\n";
Output
1887
Step by Step
| Step | $carry (before) | $price | Returned |
|---|---|---|---|
| 1 | 0 (initial) | 499 | 499 |
| 2 | 499 | 1299 | 1798 |
| 3 | 1798 | 89 | 1887 |
For a simple sum, array_sum() is shorter. array_reduce() is valuable when the combination is more complex.
Example: Finding the Most Expensive Product
<?php
$products = [
['name' => 'Gel Pen', 'price' => 20],
['name' => 'Desk Lamp', 'price' => 1299],
['name' => 'Backpack', 'price' => 899],
];
$mostExpensive = array_reduce(
$products,
fn(?array $carry, array $product): array =>
$carry === null || $product['price'] > $carry['price'] ? $product : $carry
);
echo $mostExpensive['name'] . "\n";
Output
Desk Lamp
With no initial value, the accumulator starts as null; the first product then becomes the "best so far", and each later product replaces it only if it is more expensive.
Example: Counting by Group
<?php
$statuses = ['paid', 'pending', 'paid', 'cancelled', 'paid'];
$counts = array_reduce($statuses, function (array $carry, string $status): array {
$carry[$status] = ($carry[$status] ?? 0) + 1;
return $carry;
}, []);
print_r($counts);
Output
Array
(
[paid] => 3
[pending] => 1
[cancelled] => 1
)
The accumulator can be an array, so array_reduce() can build any structure.
Always Provide an Initial Value When Possible
<?php
var_dump(array_reduce([], fn($carry, $n) => $carry + $n));
var_dump(array_reduce([], fn($carry, $n) => $carry + $n, 0));
Output
NULL
int(0)
For an empty array, the callback never runs and the initial value is returned. Without an explicit initial value, that is null — which can cause type errors later. Pass a sensible starting value (0, '', []).
Combining Them: A Data Pipeline
<?php
$orders = [
['id' => 1, 'customer' => 'Asha', 'total' => 1250, 'status' => 'paid'],
['id' => 2, 'customer' => 'Bala', 'total' => 640, 'status' => 'pending'],
['id' => 3, 'customer' => 'Charu', 'total' => 3200, 'status' => 'paid'],
['id' => 4, 'customer' => 'Dev', 'total' => 980, 'status' => 'paid'],
];
$paidOrders = array_filter($orders, fn(array $o): bool => $o['status'] === 'paid');
$totals = array_map(fn(array $o): int => $o['total'], $paidOrders);
$revenue = array_reduce($totals, fn(int $sum, int $total): int => $sum + $total, 0);
$largest = array_reduce(
$paidOrders,
fn(?array $best, array $o): array => $best === null || $o['total'] > $best['total'] ? $o : $best
);
echo "Paid orders: " . count($paidOrders) . "\n";
echo "Revenue: ₹" . number_format($revenue) . "\n";
echo "Average order: ₹" . number_format($revenue / count($paidOrders)) . "\n";
echo "Largest: {$largest['customer']} (₹" . number_format($largest['total']) . ")\n";
Output
Paid orders: 3
Revenue: ₹5,430
Average order: ₹1,810
Largest: Charu (₹3,200)
Step-by-Step Explanation
- Filter: keep only paid orders (orders 1, 3, and 4).
- Map: turn each order into its total.
- Reduce: add the totals:
1250 + 3200 + 980 = 5430. - Reduce again: find the order with the highest total. Each step has one responsibility and produces a new array, so intermediate results can be inspected or reused. (
array_sum(array_column($paidOrders, 'total'))would compute the revenue more directly; the explicit steps illustrate the pattern.)
With the Pipe Operator (PHP 8.5+)
$revenue = $orders
|> (fn(array $o) => array_filter($o, fn($x) => $x['status'] === 'paid'))
|> (fn(array $o) => array_column($o, 'total'))
|> array_sum(...);
The pipe operator passes each result into the next step, reading top to bottom.
When to Use a foreach Loop Instead
| Use the functions when… | Use foreach when… |
|---|---|
| Each step is a short, pure transformation | The body performs several actions or side effects (saving, sending, logging) |
| You need a new array and want to keep the original | You must stop early (break) — or use array_find() in PHP 8.4+ |
| The logic fits in a small callback | The logic needs many variables or complex conditions |
Both styles are good PHP. Choose the one that makes the code easiest to read.
Common Mistakes
Wrong Argument Order
array_map($callback, $array) takes the callback first; array_filter($array, $callback) and array_reduce($array, $callback) take the array first. Mixing them up causes a TypeError.
Expecting array_filter() to Re-index
Effect: Gaps in keys and JSON objects instead of arrays. Fix: array_values(array_filter(...)).
Forgetting the Initial Value in array_reduce()
Effect: null result for empty arrays. Fix: Pass an initial value matching the expected result type.
Expecting Keys in the array_map() Callback
Effect: Only values are passed. Fix: Pass array_keys($array) as an additional array, or use foreach.
Side Effects Inside Callbacks
Effect: Hidden actions (database writes, emails) inside a "transformation" make code hard to understand. Fix: Keep callbacks pure; perform side effects in a clear loop.
Best Practices
- Use
array_map()to transform,array_filter()to select, andarray_reduce()to combine. - Write short callbacks as typed arrow functions.
- Re-index filtered lists with
array_values()when order and continuity matter. - Always give
array_reduce()an initial value. - Prefer specialized functions (
array_sum(),array_column(),array_find()) when they express the task directly. Official reference: https://www.php.net/manual/en/function.array-map.php