How to combine multiple sorting functions

Tags: ,



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.

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);


Source: stackoverflow