In PHP, how does array_multisort() sort a rowset by several columns, and what happens to the keys of the arrays it reorders?
answer
- arrays of columns, not rows
- order and flags after each array
- all arrays reordered together
- string keys kept, int keys renumbered
- ValueError: Array sizes are inconsistent
basics
~20 sarray_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
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,p17go deeper
Recall that array_multisort() sorts several aligned arrays together and that you pass columns, not rows.
Explain the argument grammar of order and flag after each array, how ties fall through to the next array, and which keys survive.
Weigh array_multisort() against uasort() for large rowsets, and catch numeric-keyed maps that would lose their ids.
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