It works like a charm in SQL:
SELECT * FROM person ORDER BY age DESC, name ASC
But what if we have such data in PHP array. How can we do the same? For example, if I have
$a = [ ['name' => 'Alfred', 'age' => 40], ['name' => 'Mark', 'age' => 40], ['name' => 'Lue', 'age' => 45], ['name' => 'Ameli', 'age' => 38], ['name' => 'Barb', 'age' => 38], ];
and I want to sort it by age in descending order and then by name in ascending order. Just like in the SQL above. So the right sequence of names is: Lue, Alfred, Mark, Ameli, Barb
. See db-fiddle.
The wrong attempt to get it in PHP is:
usort($array, function ($a, $b) { return -strnatcasecmp($a['age'], $b['age']); // desc }); usort($array, function ($a, $b) { return strnatcasecmp($a['name'], $b['name']); // asc });
Each of two usort calls works fine, but it overrides the previous result, while I want to combine them all together. Ideally, I’d like to have a function that accepts any amount of Callable to usort them all.
Please advise.
Update:
As reasonable commented bellow, array_multisort is suitable for sorting regular array like this. But I’d like to find solution to combine comparator closures
. ANY comparators, even like this one.
By the way, SQL allows to sort by expression, not only by simple field.
Advertisement
Answer
Here’s a way to do it:
function combineComparators(...$comparators) { return function($a, $b) use($comparators) { foreach($comparators as $c) { $res = $c($a, $b); if($res!=0) return $res; } return 0; }; } $a = [ ['name' => 'Alfred', 'age' => 40], ['name' => 'Mark', 'age' => 40], ['name' => 'Lue', 'age' => 45], ['name' => 'Ameli', 'age' => 38], ['name' => 'Barb', 'age' => 38], ]; $cmp1 = function ($a, $b) { return -strnatcasecmp($a['age'], $b['age']); // desc }; $cmp2 = function ($a, $b) { return strnatcasecmp($a['name'], $b['name']); // asc }; usort($a, combineComparators($cmp1, $cmp2)); var_dump($a);