Mi}{aly4

Helloworlder's blog

Multisort array in PHP

2021-06-17 array, sorting, php

We have the follow data:

Last nameFirst name
PetrovaSveta
IvanovOleg
PetrovaAnna
IvanovIvan

We need to sort it by last name and first name in ascending order. How to do that by using PHP? array_multisort will help to us!

More info about array_multisort: https://www.php.net/manual/en/function.array-multisort.php

$data = [
    ['Petrova', 'Sveta'],
    ['Ivanov', 'Oleg'],
    ['Petrova', 'Anna'],
    ['Ivanov', 'Ivan'],
];

$lastNames = array_column($data, 0);
$firstNames = array_column($data, 1);
// This fix is used for cases when main sorting specified by columns has equal values
// Without it it will lead to Fatal Error: Nesting level too deep - recursive dependency?
// Thanks to Yii community https://github.com/yiisoft/yii2/issues/8348
$indexes = range(1, count($data));

array_multisort(
    $lastNames,
    SORT_ASC,
    SORT_STRING,
    $firstNames,
    SORT_ASC,
    SORT_STRING,
    $indexes,
    SORT_ASC,
    SORT_NUMERIC,
    $data
);

Result:

Last nameFirst name
IvanovIvan
IvanovOleg
PetrovaAnna
PetrovaSveta

This example is simple, and we didn't use descending order. Let's sort last name in ascending order and first name in descending order.

array_multisort(
    $lastNames,
    SORT_ASC,
    SORT_STRING,
    $firstNames,
    SORT_DESC,
    SORT_STRING,
    $indexes,
    SORT_ASC,
    SORT_NUMERIC,
    $data
);

Result:

Last nameFirst name
IvanovOleg
IvanovIvan
PetrovaSveta
PetrovaAnna

By the way, we might don't use array_multisort. Below I show multisort without the "magic" function (thanks to Doctrine community).

$data = [
    ['Petrova', 'Sveta'],
    ['Ivanov', 'Oleg'],
    ['Petrova', 'Anna'],
    ['Ivanov', 'Ivan'],
];

$orderings = [
    'lastName' => SORT_ASC,
    'firstName' => SORT_DESC,
];

$orderFn = null;
foreach (array_reverse($orderings) as $field => $ordering) {
    $orderFn = function (array $a, array $b) use ($orderFn, $field, $ordering): int {
        switch ($field) {
            case 'lastName':
                $column = 0;
                break;
                
            case 'firstName':
                $column = 1;
                break;
                
            default:
                return 0;
        }
        
        if ($a[$column] === $b[$column]) {
            if ($orderFn !== null) {
                return $orderFn($a, $b);
            } else {
                return 0;
            }
        }
        
        return ($a[$column] <=> $b[$column]) * ($ordering === SORT_ASC ? 1 : -1);
    };
}

uasort($data, $orderFn);

Result:

Last nameFirst name
IvanovOleg
IvanovIvan
PetrovaSveta
PetrovaAnna