Linesofcode
Linesofcode

Reputation: 5903

PHP multidimensional array sort with column priority

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?

Upvotes: 0

Views: 69

Answers (2)

jspit
jspit

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

amphetamachine
amphetamachine

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

Related Questions