Сортировка массива в PHP: sort, usort и свои компараторы

Сортировка массива в PHP: sort, usort и свои компараторы Полезное

Сортировка массива в PHP — это переупорядочение элементов по значению или по ключу с помощью встроенных функций (sort, asort, ksort и родственных). Все они меняют массив на месте и возвращают true, а различаются в двух вещах: по чему сортируют (значение или ключ) и сохраняют ли связку «ключ-значение». Ниже — минимальный рабочий пример, таблица всех функций, свои компараторы через usort и оператор <=>, сортировка по полю и вопрос стабильности. Ориентир по версии — PHP 8.4.

Минимальный пример: sort

Начнем с самой частой задачи — отсортировать список чисел по возрастанию.

<?php
$nums = [5, 3, 8, 1];
sort($nums);
print_r($nums);

Вывод:

Array
(
    [0] => 1
    [1] => 3
    [2] => 5
    [3] => 8
)

Функция sort() отсортировала значения по возрастанию и заново пронумеровала ключи от нуля. Прежние ключи потеряны — для простого списка это обычно и нужно. Обратная сортировка — rsort(), она даст 8, 5, 3, 1.

Все функции сортировки в таблице

В PHP один набор функций закрывает почти все случаи. Чтобы выбрать нужную, держите в голове две оси: сортируем по значению или по ключу и сохраняем ли ключи.

Функция Сортирует по Направление Сохраняет ключи
sort значению по возрастанию нет (ключи 0..n)
rsort значению по убыванию нет
asort значению по возрастанию да
arsort значению по убыванию да
ksort ключу по возрастанию да
krsort ключу по убыванию да
usort значению, свой компаратор задает компаратор нет
uasort значению, свой компаратор задает компаратор да
uksort ключу, свой компаратор задает компаратор да
natsort значению, «естественно» по возрастанию да

Мнемоника по префиксам: a — associative (сохраняет ключи), r — reverse (по убыванию), k — key (по ключу), u — user (пользовательский компаратор). Отсюда arsort читается как «сохранить ключи + по убыванию», а uksort — «свой компаратор по ключам».

Сохранять ключи или нет: asort и ksort

Разница между sort и asort видна на ассоциативном массиве. asort() сортирует по значению, но оставляет исходные ключи на месте.

<?php
$fruit = ['banana' => 3, 'apple' => 1, 'cherry' => 2];
asort($fruit);
print_r($fruit);

Вывод:

Array
(
    [apple] => 1
    [cherry] => 2
    [banana] => 3
)

Значения выстроились по возрастанию (1, 2, 3), но ключи apple, cherry, banana сохранились. Если бы мы взяли sort(), ключи заменились бы на 0, 1, 2 и связь со словами потерялась. Для ассоциативных массивов, где ключ несет смысл, берите asort/arsort.

Когда нужен порядок именно по ключу, работают ksort и krsort:

<?php
$data = [3 => 'c', 1 => 'a', 2 => 'b'];
ksort($data);
print_r($data);

Вывод:

Array
(
    [1] => a
    [2] => b
    [3] => c
)

Ключи выстроились по возрастанию, значения поехали вместе с ними. Это удобно, например, когда массив собран по числовым идентификаторам вразнобой и его надо привести к порядку.

Флаги сравнения и естественная сортировка

По умолчанию функции используют флаг SORT_REGULAR — PHP сам решает, сравнивать как числа или как строки. С числовыми строками это иногда дает неожиданный порядок, поэтому флаг стоит задавать явно.

<?php
$v = ['10', '9', '2', '1'];
sort($v, SORT_STRING);
print_r($v);

Вывод:

Array
(
    [0] => 1
    [1] => 10
    [2] => 2
    [3] => 9
)

При SORT_STRING строки сравниваются посимвольно, поэтому '10' встает раньше '2' (первый символ 1 меньше 2). Если те же значения отсортировать с флагом SORT_NUMERIC, порядок станет числовым: 1, 2, 9, 10. Выбор флага — это выбор смысла: код артикула сортируем как строку, количество — как число.

Отдельный случай — имена вида img2, img10. «Естественный» порядок дает natsort():

<?php
$files = ['img12.png', 'img10.png', 'img2.png', 'img1.png'];
natsort($files);
print_r($files);

Вывод:

Array
(
    [3] => img1.png
    [2] => img2.png
    [1] => img10.png
    [0] => img12.png
)

natsort понимает числа внутри строк, поэтому img2 идет раньше img10, а не наоборот. Ключи сохранены, поэтому в выводе они остались от исходных позиций. Для сравнения без учета регистра есть natcasesort().

Свой компаратор: usort и оператор spaceship

Когда порядок задается не самим значением, а вычислением, нужен usort() со своей функцией сравнения. Компаратор получает два элемента и должен вернуть отрицательное число, ноль или положительное — «меньше», «равно», «больше». Удобнее всего вернуть это оператором «космический корабль» <=> (доступен с PHP 7.0), который как раз выдает -1, 0 или 1.

<?php
$people = [
    ['name' => 'Ann', 'age' => 30],
    ['name' => 'Bob', 'age' => 25],
    ['name' => 'Cy',  'age' => 40],
];
usort($people, fn($a, $b) => $a['age'] <=> $b['age']);
print_r(array_column($people, 'age'));

Вывод:

Array
(
    [0] => 25
    [1] => 30
    [2] => 40
)

Массив отсортирован по полю age по возрастанию. Чтобы получить убывание, достаточно поменять аргументы местами: $b['age'] <=> $a['age']. Как и sort, функция usort не сохраняет исходные ключи. Если ключи важны, берите uasort (свой компаратор + сохранение ключей) или uksort (свой компаратор по ключам).

Сортировка по нескольким полям сразу тоже делается через <=>, если сравнивать массивы: PHP сравнивает их поэлементно, слева направо.

<?php
$rows = [
    ['dept' => 'B', 'name' => 'Ann'],
    ['dept' => 'A', 'name' => 'Zoe'],
    ['dept' => 'A', 'name' => 'Bob'],
];
usort($rows, fn($a, $b) => [$a['dept'], $a['name']] <=> [$b['dept'], $b['name']]);
print_r(array_column($rows, 'name'));

Вывод:

Array
(
    [0] => Bob
    [1] => Zoe
    [2] => Ann
)

Сначала сравнивается dept (сотрудники отдела A идут раньше B), при равном отделе — name. Так получается сортировка «сначала по отделу, потом по имени» без вложенных условий.

Частая ошибка: sort возвращает не массив

Легко забыть, что функции сортировки меняют массив на месте, а возвращают true/false, а не отсортированные данные. Отсюда типовой промах новичка.

Неверный код:

<?php
$nums = [5, 3, 8, 1];
$sorted = sort($nums);
print_r($sorted);

Фактический результат:

1

В $sorted попало логическое true, которое print_r печатает как 1. Отсортированный массив — это сам $nums, а не то, что вернула функция.

Исправление:

<?php
$nums = [5, 3, 8, 1];
sort($nums);      // массив меняется на месте
print_r($nums);   // сюда и смотрим

Второй скрытый промах — компаратор, возвращающий булево значение: fn($a, $b) => $a > $b. Он различает только «больше» и «не больше», а случаи «меньше» и «равно» схлопываются в false, и порядок получается неверным. Возвращайте разность через <=>, а не сравнение через >.

Стабильность сортировки в PHP 8

Сортировка называется стабильной, если равные по компаратору элементы сохраняют исходный взаимный порядок. С PHP 8.0 все перечисленные функции стабильны — это гарантия языка. До PHP 8.0 порядок равных элементов был не определен и мог отличаться между версиями и платформами, поэтому в старом коде на это полагаться было нельзя. На PHP 8.4 стабильность действует по умолчанию, дополнительных флагов не требуется.

Выводы

  • Две оси выбора функции: сортируем по значению или по ключу и сохраняем ли ключи; отсюда пары sort/asort, rsort/arsort, ksort/krsort.
  • Все функции меняют массив на месте и возвращают true, а не отсортированный массив — не присваивайте результат sort() переменной как данные.
  • Флаг сравнения (SORT_STRING/SORT_NUMERIC) и natsort решают проблему числовых строк и имен вида img2/img10.
  • Произвольный порядок задают usort/uasort/uksort с компаратором на операторе <=>; для нескольких полей сравнивайте массивы поэлементно.
  • С PHP 8.0 сортировка стабильна: равные элементы держат исходный порядок; в коде под старые версии на это не рассчитывайте.

Где применяется / связь с практикой

Сортировка массивов — ежедневная работа бэкенд-разработчика: вывести список товаров по цене, отчет по дате, пользователей по нескольким полям сразу. Разобравшись с usort и оператором <=>, вы закрываете почти любой порядок вывода без внешних библиотек, а знание про сохранение ключей избавляет от «съехавших» ассоциативных массивов. Эти же приемы дальше ложатся на работу с коллекциями во фреймворках и на сортировку на стороне базы данных.

Освойте тему на практике

Системно язык и его стандартную библиотеку разбирают на курсе PHP-разработчик: от массивов и функций до ООП, баз данных и промышленных практик. Посмотреть формат и уровень подачи можно на открытых уроках — это бесплатные вебинары с разбором конкретных тем.

FAQ

Чем отличается usort от uasort и uksort?
Все три принимают свой компаратор. usort сортирует по значению и сбрасывает ключи в 0..n, uasort сортирует по значению и сохраняет ключи, uksort сортирует по ключам. Выбор зависит от того, что для вас порядок — значение или ключ — и нужны ли исходные ключи.

Как отсортировать массив по убыванию со своим компаратором?
Поменяйте местами аргументы в <=>: вместо $a['age'] <=> $b['age'] напишите $b['age'] <=> $a['age']. Для встроенных функций убывание дают варианты с r: rsort, arsort, krsort.

Нужно ли сортировать в PHP, если данные приходят из базы?
Если можете, сортируйте в SQL через ORDER BY — это эффективнее на больших выборках. Сортировка в PHP уместна для уже загруженных в память данных, для порядка по вычисляемому полю или когда источник не база (файл, API, объединение нескольких списков).

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)