Collection::sortByColumn: сортировка массива строк по нескольким колонкам
Проблема/контекст
Данные собраны из нескольких источников: остатки из внешнего API, цены из ORM, рейтинг из кеша. Отсортировать результат в SQL уже нельзя, и в коде появляется usort() с замыканием на десять строк и тремя <=>. В ядре для этого есть Bitrix\Main\Type\Collection. Класс старый, в новой документации его нет, и у sortByColumn() есть поведение, которое по сигнатуре не угадать.
Решение с кодом
<?php
declare(strict_types=1);
use Bitrix\Main\Type\Collection;
$rows = [
['ID' => 7, 'STORE' => 'Склад Б', 'QUANTITY' => '15', 'PRICE' => 990.0],
['ID' => 3, 'STORE' => 'склад а', 'QUANTITY' => '9'],
['ID' => 5, 'STORE' => 'Склад А', 'QUANTITY' => '15', 'PRICE' => 1200.0],
];
Collection::sortByColumn(
array: $rows, // массив $rows передаётся в метод по ссылке
columns: [
'QUANTITY' => [SORT_NUMERIC, SORT_DESC],
'STORE' => SORT_ASC,
],
callbacks: ['STORE' => 'mb_strtolower'], // значение колонки проходит через callback перед сравнением
defaultValueIfNotSetValue: 0, // подставляется, если колонки в строке нет
preserveKeys: true, // сохранить ключи массива
);
print_r($rows);
Короткая форма Collection::sortByColumn($rows, 'PRICE') равна ['PRICE' => SORT_ASC].
Внутри метод вынимает каждую колонку в отдельный массив и вызывает array_multisort(), передавая исходный массив последним аргументом. Отсюда четыре момента.
- Флаги сортировки те же, что у
array_multisort():SORT_NUMERIC,SORT_STRING,SORT_NATURAL,SORT_FLAG_CASE. Количество'15'и'9'безSORT_NUMERICсравнится как строки. - Массив сортируется по ссылке, метод ничего не возвращает. Без пятого аргумента числовые ключи перенумеровываются. Если ключами были ID элементов, они пропадут.
- При полном равенстве по всем колонкам
array_multisort()переходит к следующему аргументу, то есть сравнивает строки целиком как массивы. Порядок получается детерминированным, но зависит от содержимого остальных полей. Если при равенстве важен исходный порядок, добавьте последней колонкой порядковый номер. - Callback должен быть
callable. Опечатка в имени функции даётArgumentOutOfRangeException('callbacks'). Один callback строкой применяется ко всем колонкам, массив видаколонка => callbackдействует выборочно.
В том же классе есть методы для вложенных массивов:
$options = ['delivery' => ['pickup' => ['enabled' => true]]];
Collection::getByNestedKey($options, ['delivery', 'pickup', 'enabled']); // true
Collection::getByNestedKey($options, ['delivery', 'courier', 'enabled']); // null
Collection::setByNestedKey($options, ['delivery', 'courier', 'enabled'], false);
getByNestedKey() возвращает null для отсутствующего ключа, но на каждом шаге вызывает array_key_exists() для текущего значения. Если по пути встретился скаляр (['delivery' => 'off']), PHP 8 бросит TypeError. setByNestedKey() создаёт промежуточные массивы сам, а на пустом пути бросает ArgumentException.
Итог
sortByColumn() заменяет самописные компараторы для табличных данных. Указывайте SORT_NUMERIC для чисел в строках и пятый аргумент, когда ключи массива имеют значение.
Комментарии (0)
Пожалуйста, войдите в аккаунт, чтобы оставить комментарий
Оставить комментарийЗагрузка...
Пока нет ни одного комментария. Будьте первым!
Похожие советы