Перейти к основному содержимому

Упорядочивание коллекции

Сортировка — это расположение элементов коллекции в определённом порядке.

У некоторых типов есть естественный порядок. Числа упорядочены по значению: 1 идёт перед 2. Строки упорядочены по алфавиту: "four" идёт перед "one". Для всего остального, а также когда нужен другой порядок, вы задаёте свой порядок: функцию сравнения, которая смотрит на два элемента и говорит, какой из них идёт первым.

Все функции сортировки в PHP работают на месте. Они принимают массив по ссылке, меняют его и возвращают true, а не отсортированный массив. $sorted = sort($words) — классическая ошибка: $sorted становится равным true.

  • sort($s) сортирует в естественном порядке, от наименьшего к наибольшему.
  • rsort($s) сортирует в естественном порядке, от наибольшего к наименьшему.
  • usort($s, $compare) сортирует с вашей собственной функцией сравнения.

Все три перенумеровывают ключи в 0, 1, 2 и так далее.

Чтобы сохранить исходный порядок, сортируйте копию. В PHP это просто, потому что массивы — это значения: $sorted = $words; даёт $sorted собственную копию, и её сортировка оставляет $words без изменений.

Функция сравнения получает два элемента, $a и $b, и возвращает число: отрицательное, когда первым идёт $a, положительное, когда первым идёт $b, и 0, когда они равны. Оператор «космический корабль» <=> даёт ровно это число: $a <=> $b равно -1, 0 или 1.

В примерах ниже используются слова one, two, three, four.


Естественный порядок

Естественный порядок — это порядок, который есть у типа сам по себе: у чисел по значению, у строк буква за буквой.

sort() располагает массив в естественном порядке. Для порядка по убыванию есть rsort(): та же сортировка, но наибольший элемент первым. Обе меняют переданный массив, поэтому сначала скопируйте его, если исходный ещё нужен.

У ваших собственных данных, например у номера версии с главной и второстепенной частью, своего естественного порядка нет. Вы задаёте его, написав функцию сравнения, которая возвращает отрицательное число, ноль или положительное число. Оператор «космический корабль» сравнивает и целые массивы, позиция за позицией, и решает первое различие — поэтому [$major, $minor] <=> [$otherMajor, $otherMinor] сначала сравнивает числа major, а числа minor — только тогда, когда major равны.

Отсортированная копия

$sorted = $words; копирует массив, а sort() располагает копию в алфавитном порядке. Исходный массив $words выводится после этого, чтобы показать, что он не изменился.

Prompt
Создайте массив из слов one, two, three, four. Скопируйте его в новую переменную и отсортируйте копию с помощью sort(), чтобы исходный массив не изменился. Выведите отсортированную копию, затем исходный массив — каждый с помощью implode.
Вывод: PHP-скрипт
Sorted copy

Сортировка по убыванию

rsort() сортирует копию от наибольшего к наименьшему, поэтому слова идут от "two" до "four".

Prompt
Создайте массив из слов one, two, three, four. Скопируйте его в новую переменную и отсортируйте копию по алфавиту в обратном порядке с помощью rsort(). Выведите результат с помощью implode.
Вывод: PHP-скрипт
Sorted descending

Тип со своим собственным порядком

Версия — это массив с числами major и minor. compareVersion() сравнивает пары [major, minor] с помощью <=>: сначала числа major; и только когда они равны, решают числа minor. Проверка compareVersion(...) > 0 отвечает на вопрос «больше ли первая версия?». json_encode() выводит логические значения как false и true.

Prompt
Храните номера версий как массивы с ключами major и minor. Напишите функцию compareVersion, которая сравнивает две версии оператором «космический корабль», применяя его к массивам [major, minor], так что первым решает номер major, а номер minor разрешает ничью. Выведите с помощью json_encode, больше ли версия 1.2, чем 1.3, а затем — больше ли 2.0, чем 1.5.
Вывод: PHP-скрипт
A type with its own order

Свои порядки

Свой порядок — это любой порядок, который вы описываете функцией сравнения.

usort() сортирует массив на месте с помощью этой функции. Начиная с PHP 8.0 любая сортировка в PHP устойчива: элементы, которые сравниваются как равные, сохраняют свой исходный порядок. Это важно, например, при сортировке слов по длине.

Обычный случай — сортировка по одному свойству каждого элемента. Поставьте это свойство с обеих сторон оператора «космический корабль»: strlen($a) <=> strlen($b) сортирует по длине.

Для порядка по убыванию поменяйте стороны местами: $b <=> $a вместо $a <=> $b.

Чтобы отсортировать по одному признаку, а затем по другому, сравнивайте два массива: [strlen($a), $a] <=> [strlen($b), $b]. Сначала сравниваются длины; если они равны, ничью разрешают сами слова.

Сортировка с функцией сравнения

Функция сравнения возвращает strlen($a) <=> strlen($b). Отрицательный результат означает, что $a короче и идёт первым, поэтому слова оказываются от самого короткого к самому длинному.

Prompt
Создайте массив из слов aaa, bb, c. Отсортируйте его на месте с помощью usort и функции сравнения, которая возвращает strlen($a) <=> strlen($b), так что более короткие слова идут первыми. Выведите результат с помощью implode.
Вывод: PHP-скрипт
Sort with a compare function

Сортировка по длине

Слова сравниваются по длине. "one" и "two" имеют длину 3 буквы; устойчивая сортировка сохраняет "one" перед "two".

Prompt
Создайте массив из слов one, two, three, four. Отсортируйте их по длине с помощью usort и оператора «космический корабль»; сортировка устойчива, поэтому слова одинаковой длины сохраняют исходный порядок. Выведите результат с помощью implode.
Вывод: PHP-скрипт
Sort by length

Сортировка по последней букве, по убыванию

$a[-1] — это последняя буква слова: отрицательное смещение в строке отсчитывается от конца. Буквы $b и $a сравниваются в обратном порядке, поэтому первым идёт слово с наибольшей последней буквой: "four" (r), затем "two" (o), затем два слова, оканчивающихся на «e», в их исходном порядке.

Prompt
Создайте массив из слов one, two, three, four. Отсортируйте их по последней букве в порядке убывания с помощью usort: сравнивайте $b[-1] <=> $a[-1]. Выведите результат с помощью implode.
Вывод: PHP-скрипт
Sort by last letter, descending

Сортировка по длине, затем по алфавиту

Оператор «космический корабль» сравнивает массивы [strlen($a), $a] и [strlen($b), $b] позиция за позицией. Первыми решают длины. Когда два слова одинаково длинные, сравниваются сами слова, что упорядочивает их по алфавиту.

Prompt
Создайте массив из слов aaa, bb, c, b, a, aa, ccc. Отсортируйте их сначала по длине, а затем по алфавиту с помощью usort, сравнивая массивы [strlen($a), $a] и [strlen($b), $b] оператором «космический корабль». Выведите результат с помощью implode.
Вывод: PHP-скрипт
Sort by length, then alphabetically

Проверка отсортированности

Иногда нужно лишь узнать, отсортирован ли массив уже, не сортируя его.

В PHP нет встроенной функции для этого, но достаточно небольшой вспомогательной функции: пройдите по массиву, сравните каждый элемент с его правым соседом и верните false, как только пара окажется в неверном порядке. Дайте этой функции необязательную функцию сравнения, и тот же цикл проверит любой порядок. Для порядка по убыванию передайте fn($a, $b) => $b <=> $a — сравнение с переставленными сторонами.

Чтобы проверить порядок по одному свойству, например по возрасту пользователя, сравнивайте это свойство внутри функции.

Проверка по возрастанию и по убыванию

isSorted() подтверждает, что 1, 2, 3, 4 идут по возрастанию. С функцией сравнения $b <=> $a она подтверждает, что 4, 3, 2, 1 идут по убыванию.

Prompt
Напишите вспомогательную функцию isSorted, которая принимает массив и необязательную функцию сравнения (по умолчанию — оператор «космический корабль») и возвращает false, как только элемент оказывается больше своего правого соседа. Проверьте, идут ли 1, 2, 3, 4 по возрастанию, а затем — идут ли 4, 3, 2, 1 по убыванию, передав fn($a, $b) => $b <=> $a. Выведите оба ответа с помощью json_encode.
Вывод: PHP-скрипт
Ascending and descending check

Проверка порядка по полю

Пользователи — это Alice (24), Bob (31) и Charlie (29). Функция сравнения смотрит только на 'age'. Bob старше Charlie, поэтому массив не отсортирован по возрасту и программа выводит false.

Prompt
Напишите вспомогательную функцию isSortedBy, которая принимает массив и функцию сравнения и проверяет каждую пару соседей. Создайте массив пользователей с ключами name и age: Alice 24, Bob 31 и Charlie 29. Проверьте, отсортированы ли пользователи по возрасту, сравнивая только поле age, и выведите ответ с помощью json_encode.
Вывод: PHP-скрипт
Check order by a field

Обратный порядок

Разворот ставит последний элемент первым, а первый — последним.

В отличие от функций сортировки, array_reverse() не меняет переданный массив: она возвращает развёрнутую копию, а исходный массив сохраняет свой порядок.

Развёрнутая копия

Выводится копия, созданная array_reverse(), а затем исходный массив, чтобы показать, что он не изменился.

Prompt
Создайте массив из слов one, two, three, four. Постройте перевёрнутую копию с помощью array_reverse, чтобы исходный массив остался прежним. Выведите перевёрнутую копию, затем исходный массив — каждый с помощью implode.
Вывод: PHP-скрипт
Reversed copy

Случайный порядок

Перемешивание располагает элементы в случайном порядке.

Функция shuffle() в PHP работает как функции сортировки: на месте, с тем массивом, который вы передаёте. Перемешивайте копию, если исходный массив ещё нужен.

shuffle() берёт случайные числа из того же генератора, что и mt_rand(). Инициализируйте этот генератор вызовом mt_srand(seed), и вы будете получать один и тот же «случайный» порядок при каждом запуске программы с этим начальным значением. Это полезно для тестов и для примеров вроде этого. Без фиксированного начального значения порядок меняется при каждом запуске.

Перемешанная копия

mt_srand(14) фиксирует начальное значение, а затем shuffle() перемешивает копию слов. Она идёт от последней позиции к первой и меняет каждый элемент со случайно выбранным элементом на этой же позиции или раньше. С этим начальным значением результат при каждом запуске — two, three, one, four.

Prompt
Создайте массив из слов one, two, three, four. Инициализируйте генератор случайных чисел вызовом mt_srand(14), скопируйте массив в новую переменную и перемешайте копию с помощью shuffle(). Выведите перемешанную копию с помощью implode.
Вывод: PHP-скрипт
Shuffled copy