Skip to main content

Ordering a Collection

Sorting means putting the elements of a collection in a certain order.

Some types have a natural order. Numbers are ordered by value: 1 comes before 2. Strings are ordered alphabetically: "four" comes before "one". For everything else, or when you want a different order, you give a custom order: a compare function that looks at two elements and says which one comes first.

PHP's sort functions all work in place. They take the array by reference, change it, and return true — not the sorted array. $sorted = sort($words) is a classic mistake: $sorted becomes true.

  • sort($s) sorts in natural order, smallest first.
  • rsort($s) sorts in natural order, biggest first.
  • usort($s, $compare) sorts with your own compare function.

All three renumber the keys to 0, 1, 2 and so on.

To keep the original order, sort a copy. That is easy in PHP, because arrays are values: $sorted = $words; gives $sorted its own copy, and sorting it leaves $words untouched.

A compare function gets two elements, $a and $b, and returns a number: negative when $a comes first, positive when $b comes first, and 0 when they are equal. The spaceship operator <=> gives exactly that number: $a <=> $b is -1, 0 or 1.

The examples below use the words one, two, three, four.


Natural Order

Natural order is the order a type has on its own: numbers by value, strings letter by letter.

sort() puts an array in natural order. For descending order there is rsort(): the same sort, biggest element first. Both change the array you pass in, so copy it first when you still need the original.

Your own data, such as a version number with a major and a minor part, has no natural order of its own. You give it one by writing a compare function that returns a negative number, zero or a positive number. The spaceship operator also compares whole arrays, position by position, and the first difference decides — so [$major, $minor] <=> [$otherMajor, $otherMinor] compares the major numbers first, and the minor numbers only when the majors are equal.

Sorted copy

$sorted = $words; copies the array, and sort() puts the copy in alphabetical order. The original $words array is printed afterwards to show it did not change.

Prompt
Make an array of the words one, two, three, four. Copy it into a new variable and sort the copy with sort(), so the original array is not changed. Print the sorted copy, then the original, each with implode.
Output: PHP script
Sorted copy

Sorted descending

rsort() sorts the copy biggest first, so the words go from "two" down to "four".

Prompt
Make an array of the words one, two, three, four. Copy it into a new variable and sort the copy in descending alphabetical order with rsort(). Print the result with implode.
Output: PHP script
Sorted descending

A type with its own order

A version is an array with a major and a minor number. compareVersion() compares [major, minor] pairs with <=>: the major numbers first; only when they are equal do the minor numbers decide. Checking compareVersion(...) > 0 answers "is the first version greater?". json_encode() prints the booleans as false and true.

Prompt
Store version numbers as arrays with the keys major and minor. Write a compareVersion function that compares two versions with the spaceship operator over [major, minor] arrays, so the major number decides first and the minor number breaks a tie. Print with json_encode whether version 1.2 is greater than 1.3, then whether 2.0 is greater than 1.5.
Output: PHP script
A type with its own order

Custom Orders

A custom order is any order you describe with a compare function.

usort() sorts an array in place with that function. Since PHP 8.0 every PHP sort is stable: elements that compare as equal keep their original order. That matters, for example, when sorting words by length.

The everyday case is sorting by one property of each element. Put that property on both sides of the spaceship operator: strlen($a) <=> strlen($b) sorts by length.

For descending order, swap the two sides: $b <=> $a instead of $a <=> $b.

To sort by one thing and then by another, compare two arrays: [strlen($a), $a] <=> [strlen($b), $b]. The lengths are compared first; if they are equal, the words themselves break the tie.

Sort with a compare function

The compare function returns strlen($a) <=> strlen($b). A negative result means $a is shorter and comes first, so the words end up from shortest to longest.

Prompt
Make an array of the words aaa, bb, c. Sort it in place with usort and a compare function that returns strlen($a) <=> strlen($b), so shorter words come first. Print the result with implode.
Output: PHP script
Sort with a compare function

Sort by length

The words are compared by length. "one" and "two" are both 3 letters long; the stable sort keeps "one" before "two".

Prompt
Make an array of the words one, two, three, four. Sort them by length with usort and the spaceship operator; the sort is stable, so words of the same length keep their original order. Print the result with implode.
Output: PHP script
Sort by length

Sort by last letter, descending

$a[-1] is the last letter of a word: a negative string offset counts from the end. The letters of $b and $a are compared in swapped order, so the word with the largest last letter comes first: "four" (r), then "two" (o), then the two words ending in "e" in their original order.

Prompt
Make an array of the words one, two, three, four. Sort them by their last letter in descending order with usort: compare $b[-1] <=> $a[-1]. Print the result with implode.
Output: PHP script
Sort by last letter, descending

Sort by length, then alphabetically

The spaceship operator compares the arrays [strlen($a), $a] and [strlen($b), $b] position by position. The lengths decide first. When two words are equally long, the words themselves are compared, which orders them alphabetically.

Prompt
Make an array of the words aaa, bb, c, b, a, aa, ccc. Sort them first by length and then alphabetically with usort, comparing the arrays [strlen($a), $a] and [strlen($b), $b] with the spaceship operator. Print the result with implode.
Output: PHP script
Sort by length, then alphabetically

Check Sorted Order

Sometimes you only want to know whether an array is already sorted, without sorting it.

PHP has no built-in function for this, but a small helper is enough: walk the array, compare every element with its right neighbour, and return false as soon as a pair is in the wrong order. Give the helper an optional compare function and the same loop checks any order. For descending order pass fn($a, $b) => $b <=> $a — the swapped comparison.

To check the order of one property, such as a user's age, compare that property inside the function.

Ascending and descending check

isSorted() confirms that 1, 2, 3, 4 are ascending. With the compare function $b <=> $a it confirms that 4, 3, 2, 1 are descending.

Prompt
Write an isSorted helper that takes an array and an optional compare function (default: the spaceship operator) and returns false as soon as an element compares greater than its right neighbour. Check whether 1, 2, 3, 4 are in ascending order, then whether 4, 3, 2, 1 are in descending order by passing fn($a, $b) => $b <=> $a. Print both answers with json_encode.
Output: PHP script
Ascending and descending check

Check order by a field

The users are Alice (24), Bob (31) and Charlie (29). The compare function looks only at 'age'. Bob is older than Charlie, so the array is not sorted by age and the program prints false.

Prompt
Write an isSortedBy helper that takes an array and a compare function and checks every pair of neighbours. Make an array of users with the keys name and age: Alice 24, Bob 31 and Charlie 29. Check whether the users are sorted by age, comparing only the age field, and print the answer with json_encode.
Output: PHP script
Check order by a field

Reverse Order

Reversing puts the last element first and the first element last.

Unlike the sort functions, array_reverse() does not change the array you give it: it returns a reversed copy, and the original keeps its order.

Reversed copy

The copy made by array_reverse() is printed, then the original is printed to show it is unchanged.

Prompt
Make an array of the words one, two, three, four. Build a reversed copy with array_reverse, so the original stays the same. Print the reversed copy, then the original, each with implode.
Output: PHP script
Reversed copy

Random Order

Shuffling puts the elements in a random order.

PHP's shuffle() works like the sort functions: in place, on the array you pass in. Shuffle a copy if you still need the original.

shuffle() takes its random numbers from the same generator as mt_rand(). Seed that generator with mt_srand(seed) and you get the same "random" order every time the program runs with that seed. This is useful for tests and for examples like this one. Without a fixed seed the order changes on every run.

Shuffled copy

mt_srand(14) fixes the seed, then shuffle() mixes a copy of the words. It walks from the last position to the first and swaps each element with a randomly chosen one at or before it. With this seed the result is two, three, one, four on every run.

Prompt
Make an array of the words one, two, three, four. Seed the random generator with mt_srand(14), copy the array into a new variable and shuffle the copy with shuffle(). Print the shuffled copy with implode.
Output: PHP script
Shuffled copy