skip to content

In PHP, how would you summarise a bakery app's orders by customer using array_column(), array_combine() and array_reduce(), and where does each fall short?

level: middleimportance: should knowfreq 40%

answer

  1. array_column(rows, column, index)
  2. duplicate index keys: last row wins
  3. array_combine throws ValueError on count mismatch
  4. array_reduce(array, fn($carry, $item), initial)
  5. reduce callback never sees the key

basics

~20 s

array_column() extracts one field, optionally keyed by another, but duplicate index values overwrite, so it cannot group. array_reduce() with an [] initial value builds per-customer totals; array_combine() pairs two lists and throws ValueError on a length mismatch.

solid answer

~40 s

`array_column($orders, 'total', 'order_id')` pulls one field from every row, arrays or objects' public properties, keyed by another field; with `null` as the column it returns whole rows re-keyed, which is handy for an ID lookup. It cannot group: when two rows share the index value, the **last row wins**, so `array_column($orders, 'total', 'customer_id')` keeps one order per customer. Grouping needs accumulation: `array_reduce($orders, fn ($carry, $o) => ..., [])` with an explicit `[]` initial value, adding each total to `$carry[$o['customer_id']]`, or a plain `foreach`. The reduce callback receives only the carry and the item, never the key. `array_combine($ids, $names)` builds a map from two parallel lists and, since PHP 8.0, throws `ValueError` when their lengths differ. For money, sum integer cents, not floats.

code

php · 21 lines
php
<?php
declare(strict_types=1);

$orders = [
    ['order_id' => 5012, 'customer_id' => 17, 'total_cents' => 1850],
    ['order_id' => 5013, 'customer_id' => 42, 'total_cents' => 900],
    ['order_id' => 5014, 'customer_id' => 17, 'total_cents' => 450],
];

$byId = array_column($orders, null, 'order_id');            // 5012 => row, ...
$lossy = array_column($orders, 'total_cents', 'customer_id'); // [17 => 450, 42 => 900]

$totals = array_reduce($orders, function (array $carry, array $o): array {
    $carry[$o['customer_id']] = ($carry[$o['customer_id']] ?? 0) + $o['total_cents'];
    return $carry;
}, []);                                                       // [17 => 2300, 42 => 900]

$names = array_combine([17, 42], ['Ana', 'Ben']);
foreach ($totals as $id => $cents) {
    printf("%s: %.2f\n", $names[$id], $cents / 100);          // Ana: 23.00, Ben: 9.00
}

go deeper

for a junior

Recall what array_column(), array_combine() and array_reduce() each return, and that array_combine() needs lists of equal length.

for a middle

Explain why duplicate index values overwrite in array_column(), why array_reduce() needs an initial value and a returned carry, and the ValueError from array_combine().

for a senior

Build reports that group correctly, validate duplicate IDs, sum in integer cents, and choose foreach over reduce when it reads more clearly.

for a principal

Decide when summaries belong in PHP array code and when the database should aggregate, weighing data volume, consistency and maintainability.

## The task A bakery-delivery app loads today's orders as rows, each an associative array such as `['order_id' => 5012, 'customer_id' => 17, 'total_cents' => 1850]`. The report needs: total spent per customer, the customer's name next to each total, and a quick way to look an order up by ID. PHP's array helpers cover each step, but each has an edge that interviewers probe. ## array_column(): one field from every row `array_column(array $array, int|string|null $column_key, int|string|null $index_key = null): array` treats its input as a list of rows: - `array_column($orders, 'total_cents')` gives a list of totals, numbered from 0. - `array_column($orders, 'total_cents', 'order_id')` keys each total by its order ID. - `array_column($orders, null, 'order_id')` returns the **whole rows** keyed by order ID, the standard way to build an ID lookup table. - Rows may be objects; public properties are read directly. - Rows that lack the column are skipped silently, which can hide a typo in the column name. The limitation is duplicate index values. Keys are unique, so if customer 17 placed three orders, `array_column($orders, 'total_cents', 'customer_id')` keeps only the **last** of them. It extracts; it does not group. ## array_reduce(): folding rows into a summary `array_reduce(array $array, callable $callback, mixed $initial = null): mixed` calls `$callback($carry, $item)` for each element, where `$carry` is the previous return value, starting from `$initial`: ```php $byCustomer = array_reduce( $orders, function (array $carry, array $o): array { $carry[$o['customer_id']] = ($carry[$o['customer_id']] ?? 0) + $o['total_cents']; return $carry; }, [], ); ``` Points to get right: 1. **Pass the initial value.** Without it, `$carry` starts as `null`, and a callback typed `array $carry` throws `TypeError` on the first call. For an empty input with no initial value the result is `null`. 2. **Return the carry every time.** Forgetting `return $carry;` turns the next carry into `null`. 3. **No key.** The callback receives the item but not its key. If the key matters, loop with `foreach` instead, or reduce over `array_keys()` and look values up. Many teams find the equivalent `foreach` clearer for grouping, since it writes into the result directly without threading a carry through a callback. Both are correct; readability decides. ## array_combine(): two lists into one map To show names, suppose the customer service returns two parallel lists, `$ids = [17, 42]` and `$names = ['Ana', 'Ben']`. `array_combine($ids, $names)` returns `[17 => 'Ana', 42 => 'Ben']`. | Input problem | Result since PHP 8.0 | |---|---| | lists of different length | `ValueError`: argument #1 ($keys) and argument #2 ($values) must have the same number of elements | | duplicate values in `$keys` | the later value wins for that key | | key values that are numeric strings | converted to integer keys, as for any array key | Before PHP 8.0 a length mismatch returned `false`; code that still checks `=== false` never sees that branch now. ## Putting it together The report uses each helper for what it is good at: - `array_column($orders, null, 'order_id')` for the lookup table. - `array_reduce()` or a `foreach` for the per-customer totals. - `array_combine($ids, $names)` to map customer IDs to names, then read `$names[$id]` while printing totals. - `array_sum(array_column($orders, 'total_cents'))` for the day's grand total. ## Money and types Totals are summed as **integer cents**. Summing float prices such as `18.5` repeatedly accumulates rounding error, and floats used as array keys are truncated, so they make poor identifiers as well. Keep IDs as integers or strings and amounts as integers until they are formatted for display.

  • What happens if array_reduce() is called without an initial value and the callback declares array $carry?
    The first call receives `null` as the carry, so a parameter typed `array $carry` throws `TypeError`. Pass `[]` as the third argument. Without an initial value an empty input also returns `null` rather than an empty summary.
  • How do you build an order lookup table keyed by order ID in one call?
    Call `array_column($orders, null, 'order_id')`. A `null` column key returns each complete row, and the index key re-keys the rows by their ID. Because keys are unique, a duplicated ID would keep only the last row, which is a useful signal to validate.

saying these in an interview costs you the question

  • array_column() with an index key groups rows that share that index.
  • array_combine() returns false when the lists differ in length.
  • array_reduce() passes each element's key to the callback.
  • array_reduce() starts from an empty array when no initial value is given.
  • Summing float prices is fine for money totals.