Сравните как работает SQL Код (SQL): INSERT INTO person (`name`, `age`) VALUES ('Alfred', '40'), ('Mark', '40'), ('Lue', '45'), ('Ameli', '38'), ('Barb', '38'); SELECT * FROM person ORDER BY age DESC, name ASC и как работает неуклюжая попытка воспроизвести это на голом PHP: Код (PHP): usort($array,function($a, $b){ return -strnatcasecmp($a['age'], $b['age']);// desc }); usort($array,function($a, $b){ return strnatcasecmp($a['name'], $b['name']);// asc }); Желаемый результат вот такой: Lue, Alfred, Mark, Ameli, Barb Но т.к. следующая сортировка затирает результат предшествующей, то получается: Alfred, Ameli, Barb, Lue, Mark Хотелось бы написать универсальную функцию, которая будет соединять переданные в неё замыкания и производить накопительную сортировку. Важное дополнение: array_multisort() клёвый, но хочется комбинировать функции. Это бы дало бОльшую гибкость.
@acho это решение для конкретного примера, да. Но было бы ещё круче получить решение в общем виде для двух и более функций-компараторов.
up. PHP: /** @var array */ protected $data = []; /** @var array */ protected $comparators = []; public function __construct($data = null) { $this->data = $data ?? []; return $this; } public function orderBy($field, $order = 'asc') { $multiplier = strcasecmp($order, 'asc') ? 1 : -1; $this->comparators[] = function ($a, $b) use ($field, $multiplier) { return strcasecmp($b[$field], $a[$field]) * $multiplier; }; return $this; } // . . . public function get() { usort( $this->data, function ($a, $b) { foreach ($this->comparators as $c) { $res = $c($a, $b); if ($res != 0) return $res; } return 0; } ); return $this->data; } пример использования (задача из реальной практики) PHP: $recordset = getFromExternalSource(); $recordset = data($recordset) ->whereIn('name', [$fname, $lname]) ->whereAnyIn('addresses', [$state, $county]) ->orderByBetterMatch('addresses', [$state, $county], 'desc') // накапливаем... ->orderByExactMatch('name', "{$fname} {$lname}", 'desc') // накапливаем... ->limit(100) ->get(); // применяем все накопленные сортировки и отдаём
Какие ф-ции? array_multisort поддерживает не только направления, но и типы сортировки: PHP: array_multisort( $b, SORT_DESC, SORT_NUMERIC, $a, SORT_ASC, SORT_STRING ); Для большей наглядности вместо 38 можно взять к примеру 380, чтобы увидеть натуральную сортировку.
P.S. Для использования произвольных ф-ций в доках рекомендуют сортировать по измененной копии: PHP: array_multisort( $b, SORT_DESC, SORT_NUMERIC, array_map('strtolower', $a), SORT_ASC, SORT_STRING, $a );
а поддерживает он такое: сортировать по порядку, но нужное значение удерживать первым? или сортировать по полю-подмассиву, по максимальной похожести элементов на образец? и т.п. короче, @miketomlin, спасибо за желание помочь, но нет. тебя наверное тоже бесит когда ты сказал "пиво не пью", а тебе в ответ "ну вот есть хорошее пиво", да?
Я не думал, что мой пост может кого-то взбесить. На решение задачки, естественно, не претендую. Просто решил дополнить твое «важное дополнение». Ну и вопрос про ф-ции задал не ради фигуры речи. --- Добавлено --- P.S. Меня можно Миша/Михаил звать. --- Добавлено --- P.P.S. Если «просто потрещать» в создаваемых тобой темах не приветствуется, ты пиши
Очень приятно, я Саша Да я не против поболтать, наверное получилось слишком резко, сорри. Так-то я всегда рад общению. --- Добавлено --- В очередной раз заметил, что сам факт обсуждения уже помогает снять шоры с глаз. Понял, что limit надо тоже отложенным делать. Потому что если резать неотсортированные данные, можно потерять нужное. правильная последовательность: фильтры → сортировка → обрезание
PHP: <?php use Tuck\Sort\Sort; require __DIR__.'/vendor/autoload.php'; $array = [ ['name' => 'Alfred', 'age' => 40], ['name' => 'Mark', 'age' => 40], ['name' => 'Lue', 'age' => 45], ['name' => 'Ameli', 'age' => 38], ['name' => 'Barb', 'age' => 38], ]; $result = Sort::chain() ->desc(function ($item) { return $item['age']; }) ->asc(function ($item) { return $item['name']; })->values($array); foreach ($result as $item) { echo $item['name'].' '.$item['age'].PHP_EOL; } Код (Text): Lue 45 Alfred 40 Mark 40 Ameli 38 Barb 38 https://github.com/rosstuck/sort вроде оно ) ну как, если хочешь универсальности, то наверное придется форкнуть и навернуть абстракций, но в целом довольно близко, если я правильно понял что тебе нужно.