0

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:

  1. Grand total <-- The main priority ASC
  2. Lowest order date <-- If grand total equal, check this
  3. 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?

1
  • 1
    Don't add the results of comparison. Commented Dec 8, 2022 at 16:39

2 Answers 2

1

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;
});
Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.