skip to content

In PHP, how does array_multisort() sort a rowset by several columns, and what happens to the keys of the arrays it reorders?

level: middleimportance: nice to knowfreq 20%

answer

  1. arrays of columns, not rows
  2. order and flags after each array
  3. all arrays reordered together
  4. string keys kept, int keys renumbered
  5. ValueError: Array sizes are inconsistent

basics

~20 s

array_multisort() takes several equal-length arrays, each optionally followed by SORT_ASC/SORT_DESC and a flag, sorts by the first and breaks ties with the next, and reorders all of them together. String keys survive; integer keys are renumbered.

solid answer

~40 s

`array_multisort()` sorts **columns**, not rows. You extract one array per sort key, usually with `array_column()`, then call `array_multisort($score, SORT_DESC, $seconds, SORT_ASC, $rows)`. PHP orders by the first array, uses the second for ties, and applies the same permutation to every array passed, so passing `$rows` last reorders the rows themselves. Each array can be followed by a sort order (`SORT_ASC` by default) and a sort flag (`SORT_REGULAR` by default), in either order. Everything is modified in place by reference and the function returns `true`. Arrays of different lengths throw a `ValueError` with the message "Array sizes are inconsistent". String keys are kept, integer keys are renumbered from 0.

code

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

$rows = [
    ['id' => 'p17', 'score' => 820, 'seconds' => 41],
    ['id' => 'p03', 'score' => 950, 'seconds' => 58],
    ['id' => 'p42', 'score' => 820, 'seconds' => 37],
];

$score   = array_column($rows, 'score');
$seconds = array_column($rows, 'seconds');

array_multisort($score, SORT_DESC, $seconds, SORT_ASC, $rows);

echo implode(',', array_column($rows, 'id')), PHP_EOL; // p03,p42,p17

go deeper

for a junior

Recall that array_multisort() sorts several aligned arrays together and that you pass columns, not rows.

for a middle

Explain the argument grammar of order and flag after each array, how ties fall through to the next array, and which keys survive.

for a senior

Weigh array_multisort() against uasort() for large rowsets, and catch numeric-keyed maps that would lose their ids.

for a principal

Prefer the form a team can read and test; positional multisort calls are compact but easy to break when a column is added, so wrap them in a named helper.

## What array_multisort does `array_multisort()` sorts several arrays **as if they were the columns of one table**. Element `i` of each array belongs to row `i`. The function: 1. Orders the rows by the values of the **first** array. 2. For rows whose first values tie, looks at the **second** array, then the third, and so on (a lexicographic sort). 3. Applies the resulting permutation to **every** array you passed, so the columns stay aligned. All arguments are taken by reference and modified in place; the return value is `true`. Because it compares with built-in comparators rather than calling a PHP callback for each comparison, it is a common choice for sorting large tabular arrays. ## The argument grammar The signature is effectively `array_multisort(array &$array1, $order?, $flags?, array &...$more)`: - After each array you may give a **sort order**, `SORT_ASC` (the default) or `SORT_DESC`. - After each array you may give a **sort flag**: `SORT_REGULAR` (default), `SORT_NUMERIC`, `SORT_STRING`, `SORT_LOCALE_STRING` or `SORT_NATURAL`, optionally OR-ed with `SORT_FLAG_CASE`. - Order and flag can appear in either order and apply to the array just before them. - Giving the same kind twice for one array (two orders, say) throws a `TypeError`: the argument "must be an array or a sort flag that has not already been specified". - If the arrays have different lengths, PHP throws a `ValueError` with the message "Array sizes are inconsistent". Constants such as `SORT_DESC` can be passed even though the parameters are by-reference, because the function is declared to *prefer* references rather than require them. ## Sorting a leaderboard rowset Given rows like `['id' => 'p17', 'score' => 820, 'seconds' => 41]`, the pattern is: ```php $score = array_column($rows, 'score'); $seconds = array_column($rows, 'seconds'); array_multisort($score, SORT_DESC, $seconds, SORT_ASC, $rows); ``` After the call, `$rows` is ordered by score descending, then by time ascending. `$score` and `$seconds` are sorted too; they are scratch arrays you usually discard. The last argument `$rows` is compared only if both earlier columns tie, so for deterministic results make the earlier columns decisive, or add a unique column such as the id. ## What happens to keys | Key type in a reordered array | After `array_multisort()` | |---|---| | string keys (`'p17' => …`) | kept, moved together with their values | | integer keys (`0 => …`, `5 => …`) | renumbered `0..n-1` in the new order | So a rowset that is a plain list comes back as a list, and a rowset keyed by player id keeps its ids. A map keyed by **numeric** ids, such as `[1017 => $row]`, is the trap: those keys are integers, so they are renumbered and the ids are lost. Store the id inside each row, or use `uasort()`, which keeps every key. ## Mistakes that show up in review 1. **Passing only the columns.** If `$rows` is not among the arguments, the column arrays are sorted and the rows stay in their old order. 2. **Passing the rowset first.** `array_multisort($rows, …)` compares whole rows as arrays, element by element, which is rarely the intended key. 3. **Building columns from a filtered copy.** If a column has fewer elements than `$rows`, the call throws the `ValueError` instead of sorting. 4. **Expecting numeric ids to survive.** Integer keys are renumbered, as the table above shows. ## array_multisort or uasort | Concern | `array_multisort()` | `uasort()` with `<=>` | |---|---|---| | Inputs | one array per sort key, same length | the rows themselves | | Direction per key | `SORT_ASC` / `SORT_DESC` arguments | operand order in the comparator | | Integer keys | renumbered | kept | | Per-comparison cost | built-in comparator | a PHP callback call | | Readability | positional arguments | one explicit expression | Choose `array_multisort()` for large, column-shaped data where the columns already exist or are cheap to extract. Choose `uasort()` when keys must survive whatever their type, or when the rule is easier to read as one comparator.

  • Why does a leaderboard keyed by numeric player ids lose its ids in array_multisort()?
    Because `array_multisort()` keeps only string keys; integer keys are renumbered from 0 in the new order. An array keyed by `1017`, `2044` and so on therefore comes back as a list. Keep the id inside each row, or sort with `uasort()`, which keeps every key.
  • What does passing SORT_NATURAL | SORT_FLAG_CASE after a column do in array_multisort()?
    It sets that column's comparison mode: digit runs compare as numbers and ASCII case is ignored, exactly as with `sort()`. The flag applies only to the array just before it; other columns keep their own order and flag, which default to `SORT_ASC` and `SORT_REGULAR`.

saying these in an interview costs you the question

  • array_multisort() takes an array of rows and a column name
  • array_multisort() returns the sorted array
  • It keeps every key, integer keys included
  • Only the first array is reordered; the others are untouched
  • Arrays of different lengths are padded rather than rejected