I have the following array:
$data =
[
[
'totals' => ['grand_total' => 729.81, 'lowest_order_date' => '', 'lowest_created_at' => '2022-11-23 15:23:06'],
],
[
'totals' => ['grand_total' => 746.03, 'lowest_order_date' => '', 'lowest_created_at' => '2022-11-22 19:46:13'],
],
[
'totals' => ['grand_total' => 729.81, 'lowest_order_date' => '2022-11-22 15:30:22', 'lowest_created_at' => '2022-11-21 14:25:07'],
],
];
And I'm using the following code to sort it:
usort($data, function ($a, $b)
{
return
($a['totals']['grand_total'] <=> $b['totals']['grand_total'])
($a['totals']['lowest_order_date'] <=> $b['totals']['lowest_order_date'])
($a['totals']['lowest_created_at'] <=> $b['totals']['lowest_created_at']);
});
I was expecting this function to sort the content by the following priority:
- Grand total <-- The main priority ASC
- Lowest order date <-- If grand total equal, check this
- Lowest created at <-- If grand total equal, check this
However, the output puts the 746.03
as first result:
$data =
[
[
'totals' => ['grand_total' => 746.03, 'lowest_order_date' => '', 'lowest_created_at' => '2022-11-22 19:46:13'],
],
[
'totals' => ['grand_total' => 729.81, 'lowest_order_date' => '', 'lowest_created_at' => '2022-11-23 15:23:06'],
],
[
'totals' => ['grand_total' => 729.81, 'lowest_order_date' => '2022-11-08 15:30:22', 'lowest_created_at' => '2022-11-21 14:25:07'],
],
];
Expected output:
$data =
[
[
'totals' => ['grand_total' => 729.81, 'lowest_order_date' => '', 'lowest_created_at' => '2022-11-23 15:23:06'],
],
[
'totals' => ['grand_total' => 729.81, 'lowest_order_date' => '2022-11-22 15:30:22', 'lowest_created_at' => '2022-11-21 14:25:07'],
],
[
'totals' => ['grand_total' => 746.03, 'lowest_order_date' => '', 'lowest_created_at' => '2022-11-22 19:46:13'],
],
];
By looking at the problem, it seems to me logical that I first need to sort only by grand_total
and then perform sort on lowest_order_date
& lowest_created_at
if the grand_total
is equal. Is there an easy way to do it by using the sort
functions?
CodePudding user response:
Yes, you can sort by multiple keys by defining the sort function to first sort by the grand total, and then by the other keys if the grand total is equal.
Here's one way to do it:
usort($data, function ($a, $b) {
// Sort by grand total first
if ($a['totals']['grand_total'] != $b['totals']['grand_total']) {
return $a['totals']['grand_total'] <=> $b['totals']['grand_total'];
}
// If the grand total is equal, sort by lowest_order_date
if ($a['totals']['lowest_order_date'] != $b['totals']['lowest_order_date']) {
return $a['totals']['lowest_order_date'] <=> $b['totals']['lowest_order_date'];
}
// If the grand total and the lowest_order_date are equal, sort by lowest_created_at
return $a['totals']['lowest_created_at'] <=> $b['totals']['lowest_created_at'];
});
This will sort the array in the order you specified.
CodePudding user response:
Adding together the results from <=>
is probably not what you want, as it could return a -1
and a 1
and those negate to 0. Same goes for the return from strcmp()
and friends.
usort($data, function ($a, $b)
{
// First priority
if (($result = ($a['totals']['grand_total'] <=> $b['totals']['grand_total'])) !== 0) {
return $result;
}
// Second priority
if (($result = ($a['totals']['lowest_order_date'] <=> $b['totals']['lowest_order_date'])) !== 0) {
return $result;
}
// Lowest priority
return $a['totals']['lowest_created_at'] <=> $b['totals']['lowest_created_at']);
});
DRYing up the code:
usort($data, function ($a, $b)
{
// Keys in 'totals', highest-to-lowest sort priority
$sort_priority = [
'grand_total',
'lowest_order_date',
'lowest_created_at',
];
foreach ($sort_priority as $sort_key) {
if (($result = ($a['totals'][$sort_key] <=> $b['totals'][$sort_key])) !== 0) {
return $result;
}
}
// Compared all fields, they're a tie for sort order
return 0;
});
CodePudding user response:
Easy-to-remember notation for PHP multidimensional array sorting with column priority:
usort($data,function($a,$b){
return $a['totals']['grand_total'] <=> $b['totals']['grand_total']
?: $a['totals']['lowest_order_date'] <=> $b['totals']['lowest_order_date']
?: $a['totals']['lowest_created_at'] <=> $b['totals']['lowest_created_at']
;});
Another advantage is that if a column is to be sorted in descending order, only $a and $b in the row have to be swapped. Functions can also be easily embedded.