0

I'm using array_multisort (PHP 7.4) to apply a mutli-dimensional sort. I think I'm using it correctly, but the result is not what I would expect. No matter what direction I set, it only sorts ascending (even if I set both to SORT_DESC). I tried different sort flags (e.g. SORT_STRING, SORT_NATURAL), but this did not make a difference.

$definitions = [
  [
    'name' => 'Bart',
    'age' => 12,
    'location' => 'Brazil',
  ],
  [
    'name' => 'Daniel',
    'age' => 51,
    'location' => 'Brazil',
  ],
  [
    'name' => 'Adam',
    'age' => 33,
    'location' => 'France',
  ],
  [
    'name' => 'Adam',
    'age' => 44,
    'location' => 'France',
  ],
  [
    'name' => 'Adam',
    'age' => 5,
    'location' => 'France',
  ],
  [
    'name' => 'Zed',
    'age' => 21,
    'location' => 'GB',
  ],
];

$sorting = [
  [
    'field' => 'name',
    'direction' => SORT_ASC,
  ],
  [
    'field' => 'age',
    'direction' => SORT_DESC,
  ]
];


$sort_args = [];
foreach ($sorting as $sort) {
  $sort_args[] = array_column($definitions, $sort['field']);
  $sort_args[] = $sort['direction'];
  $sort_args[] = SORT_REGULAR;
}
array_multisort($definitions, ...$sort_args);

The result:

array (
  0 =>
    array (
      'name' => 'Adam',
      'age' => 5,
      'location' => 'France',
    ),
  1 =>
    array (
      'name' => 'Adam',
      'age' => 33,
      'location' => 'France',
    ),
  2 =>
    array (
      'name' => 'Adam',
      'age' => 44,
      'location' => 'France',
    ),
  3 =>
    array (
      'name' => 'Bart',
      'age' => 12,
      'location' => 'Brazil',
    ),
  4 =>
    array (
      'name' => 'Daniel',
      'age' => 51,
      'location' => 'Brazil',
    ),
  5 =>
    array (
      'name' => 'Zed',
      'age' => 21,
      'location' => 'GB',
    ),
)

What I expected:

array (
  0 =>
    array (
      'name' => 'Adam',
      'age' => 44,
      'location' => 'France',
    ),
  1 =>
    array (
      'name' => 'Adam',
      'age' => 33,
      'location' => 'France',
    ),
  2 =>
    array (
      'name' => 'Adam',
      'age' => 5,
      'location' => 'France',
    ),
  3 =>
    array (
      'name' => 'Bart',
      'age' => 12,
      'location' => 'Brazil',
    ),
  4 =>
    array (
      'name' => 'Daniel',
      'age' => 51,
      'location' => 'Brazil',
    ),
  5 =>
    array (
      'name' => 'Zed',
      'age' => 21,
      'location' => 'GB',
    ),
)

Am I missing something or is this busted?

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
bfuzze
  • 438
  • 8
  • 15
  • 1
    `array_push($sort_args, array_column($definitions, $sort['field']), $sort['direction'], SORT_REGULAR)` would be a little more concise inside of your loop. – mickmackusa Jan 19 '23 at 07:11

1 Answers1

2

You have the arguments in the wrong order. The array you wish to sort should be the last item for this type of sorting.

Example #3: https://www.php.net/manual/en/function.array-multisort.php#example-5040

You may have done this so that you can use the spread operator (which is fine). If you want to continue using the spread operator you would adjust your code as so:

$sort_args[] = &$definitions; // add this line
array_multisort(...$sort_args); // remove `$definitions` from this line

Note that we are passing $definitions as reference otherwise when we push it to $sort_args, we would be creating a copy (due to the nature of PHP).

Without passing by reference, that original array won't be sorted, only the copy we created.

Rylee
  • 1,481
  • 1
  • 6
  • 9
  • Interesting... and very confusing. I did originally try adding definitions at the end per some of the comments, but not as a reference. This function and the documentation are poorly conceived, switching the context argument based on usage... Thanks so much @Rylee! – bfuzze Jan 19 '23 at 18:13
  • @bfuzze I agree, it is quite confusing as in some examples, the array you want to sort comes first but in this case it's last for no given reason. The documentation even describes the first parameter as "An array being sorted." just to add to the confusion – Rylee Jan 19 '23 at 22:06