Sorting functions in PHP
And the Spaceship Operator πππ
--
When dealing with PHP, there are multiple ways to sort a collection of items, and sometimes itβs not clear which method we should use and why.
What is the difference between
sort()
,arsort()
anduasort()
?
Before explaining the repercussions of a
, r
, k
and u
+ sort()
function, we need to know the very basic concept of lists and maps.
List
A list is an ordered collection.
The order is sequential starting from 0 and increasing 1 by 1 for each item.
It is also possible to have duplicated items in a list.
$list = ['Croatia', 'Belgium', 'Austria', 'Belgium', 'Denmark'];
print_r($list);
Array
(
[0] => Croatia
[1] => Belgium
[2] => Austria
[3] => Belgium
[4] => Denmark
)
Map (also known as Dictionaries)
A map is an unordered collection.
Each element is composed of a key and a value.
It is not possible to have the same key more than once.
$map = [
'Croatia' => 0,
'Belgium' => 1,
'Austria' => 2,
'Denmark' => 3,
'Belgium' => 4,
];
print_r($map);
Array
(
[Croatia] => 0
[Belgium] => 4 // 'Belgium => 1' was overriden
[Austria] => 2
[Denmark] => 3
)
It was important to do a quick overview of maps and lists, they are different kinds of collections, and because of that, each one has its own specific sorting methods.
The idea of those a
, r
, k
& u
+ sort()
is to work as modifiers as follows:
R = reverse β sort in descencing order, lists & maps
K = key β sort by key, only for maps
A = associative β sort by value, only for maps
U = user-defined β defined by the user in a callback
Sorting Lists π§΅
sort() & rsort()
These methods sort lists in ascending/descending order.
# sort: Sort list by ascending order.
$sort = ['Croatia', 'Belgium', 'Austria', 'Belgium'];
sort($sort);
Array
(
[0] => Austria
[1] => Belgium
[2] => Belgium
[3] => Croatia
)
---
# rsort: Sort list by descending order.
$rsort = ['Croatia', 'Belgium', 'Austria', 'Belgium'];
rsort($rsort);
Array
(
[0] => Croatia
[1] => Belgium
[2] => Belgium
[3] => Austria
)
Sorting Maps πΊοΈ
asort(), arsort(), ksort() & krsort()
These methods sort associative arrays by key/value in ascending/descending order.
# ksort: Sort map by key in ascending order.
$ksort = [100 => 'Croatia', 200 => 'Austria', 300 => 'Belgium'];
ksort($ksort);
Array
(
[100] => Croatia
[200] => Austria
[300] => Belgium
)
---
# krsort: Sort map by key in descending order.
$krsort = [100 => 'Croatia', 200 => 'Austria', 300 => 'Belgium'];
krsort($krsort);
Array
(
[300] => Belgium
[200] => Austria
[100] => Croatia
)
---
# asort: Sort map by value in ascending order.
$asort = [100 => 'Croatia', 200 => 'Austria', 300 => 'Belgium'];
asort($asort);
Array
(
[200] => Austria
[300] => Belgium
[100] => Croatia
)
---
# arsort: Sort map by value in descending order.
$arsort = [100 => 'Croatia', 200 => 'Austria', 300 => 'Belgium'];
arsort($arsort);
Array
(
[100] => Croatia
[300] => Belgium
[200] => Austria
)
User-defined functions π©βπ»
Until now everything was nice, but as you know, usually we have a collection of complex objects, and sometimes we want to define our own sorting function, in these scenarios, the asort() method simply doesnβt work. Eg:
βSorting a collection of Product
by price in ascending order, and when more than one product has the same price, sort them alphabetically by the nameβ.
We need to find a more sophisticated approach. And this is only possible if we can define our own sorting method, in this case, we can take advantage of using the spaceship operator
! π
Basically, this operator is syntactic sugar specialized for comparisons.
The first parameter is the array we want to sort and the second is a callback that returns an integer value.
/** @var callable(mixed,mixed):int $callable */
$callable = function (mixed $a, mixed $b): int { ... }
usort(&$array, $callable): bool
The values this callable must return are -1
, 0
and 1
if the current item is less, equal or greater than the previous one, to be sorted before or after in the collection.
function compare(int $a, int $b): int
{
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
# Is the same than...
function compare(int $a, int $b): int
{
return $a <=> $b;
}
# Is the same than...
fn (int $a, int $b): int => $a <=> $b;
usort(), uksort() & uasort()
These methods sort by keys or values (preserving or resetting the keys) in a user-defined callback. Letβs see some examples:
# usort: Sort list/map by callback from the values, keys are reset.
$usort = [100 => 'Croatia', 300 => 'Austria', 200 => 'Belgium'];
usort($usort, fn (string $a, string $b): int => $a <=> $b);
Array
(
[0] => Austria
[1] => Belgium
[2] => Croatia
)
---
# uksort: Sort map by callback from the keys.
$uksort = [100 => 'Croatia', 300 => 'Austria', 200 => 'Belgium'];
uksort($uksort, fn (int $a, int $b): int => $a <=> $b);
Array
(
[100] => Croatia
[200] => Belgium
[300] => Austria
)
---
# uasort: Sort map by callback from the values preserving the keys.
$uasort = [100 => 'Croatia', 300 => 'Austria', 200 => 'Belgium'];
uasort($uasort, fn (string $a, string $b): int => $a <=> $b);
Array
(
[300] => Austria
[200] => Belgium
[100] => Croatia
)
For the sake of simplicity, I used the function: fn ($a, $b) => $a <=> $b;
if the collection was holding complex objects, it would be the same but comparing some property, it depends on your needs.
π Cheat Sheet
sort() - sort list in ascending order
rsort() - sort list in descending order
asort() - sort map by value in ascending order
ksort() - sort map by key in ascending order
arsort() - sort map by value in descending order
krsort() - sort map by key in descending order
usort() - sort list or map by value in a user-defined callback, reset keys
uksort() - sort map by keys in a user-defined callback
uasort() - sort map by value in a user-defined callback
* Take into account that the array is passed by reference to the sorting function, which means you must initialize the array in a different line, and the sorting function will mutate the original array π