Reputation: 5903
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:
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?
Upvotes: 0
Views: 69
Reputation: 7703
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.
Upvotes: 1
Reputation: 30623
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;
});
Upvotes: 1