Search code examples
phparrayssortingmultidimensional-arraysub-array

PHP usort array based on subarray elements not working right - buggy?


Here's my current code, using php 7.1.20-1+ubuntu18.04.1+deb.sury.org+1: (Four columns to sort by, 5th column just a subarray reference number.)

$dud = [[2,3,"2018-07-19","08:23",1],
    [2,3,"2018-07-19","08:30",2],
    [2,1,"2018-07-19","08:14",3],
    [2,4,"2018-07-19","07:11",4],
    [2,1,"2018-07-19","07:17",5],
    [2,9,"2018-07-19","07:31",6],
    [2,4,"2018-07-19","05:06",7],
    [2,6,"2018-07-18","08:10",8],
    [2,9,"2018-07-19","07:20",9],
    [1,7,"2018-07-19","08:27",10],
    [1,5,"2018-07-19","08:11",11],
    [1,7,"2018-07-18","08:22",12],
    [1,5,"2018-07-19","08:09",13],
    [2,6,"2018-07-18","07:12",14],
    [1,7,"2018-07-18","08:21",15],
    [1,7,"2018-07-19","07:09",16]];

usort($dud, function($a,$b){if ($a[3] !== $b[3]){return strcmp($a[3],$b[3]);}});
usort($dud, function($a,$b){if ($a[2] !== $b[2]){return strcmp($a[2],$b[2]);}});
    // usort($dud, function($a,$b){if ($a[1] !== $b[1]){return $a[1] - $b[1];}});
usort($dud, function($a,$b){if ($a[1] !== $b[1]){return strcmp($a[1],$b[1]);}});
    // usort($dud, function($a,$b){if ($a[0] !== $b[0]){return $a[0] - $b[0];}});
usort($dud, function($a,$b){if ($a[0] !== $b[0]){return strcmp($a[0],$b[0]);}});

foreach($dud as $output){
    foreach($output as $output2){
        echo "  $output2   ";
    }
    echo "<br/>";
}

I am attempting to sort the 16 subarrays, first by 4th column, then by 3rd column, then 2nd, then 1st. My output:

1 5 2018-07-19 08:09 13
1 5 2018-07-19 08:11 11
1 7 2018-07-18 08:21 15
1 7 2018-07-18 08:22 12
1 7 2018-07-19 07:09 16
1 7 2018-07-19 08:27 10
2 1 2018-07-19 08:14 3
2 1 2018-07-19 07:17 5
2 3 2018-07-19 08:23 1
2 3 2018-07-19 08:30 2
2 4 2018-07-19 07:11 4
2 4 2018-07-19 05:06 7
2 6 2018-07-18 08:10 8
2 6 2018-07-18 07:12 14
2 9 2018-07-19 07:20 9
2 9 2018-07-19 07:31 6

As is, the output has subarrays 3 and 5 out of order (07:17 should be before 08:14), subarrays 4 and 7 are out of order (05:06 should be before 07:11), and subarrays 8 and 14 are out of order (07:12 should be before 08:10). Commenting out different usort lines, it sorts column four just fine with all other usort lines commented out. Sorting just columns 1 and 4 works fine. Sorting just columns 2 and 4, subarrays 3 and 5 are out of order (07:17 should be before 8:14). Sorting just columns 3 and 4, subarrays 8 and 14 are out of order (07:12 should be before 08:10). Any idea what's going on here? I've tried making use of what info is available at: PHP Sort Array By SubArray Value but still getting a quirky oddball missort in the fourth column. Thanks much!!


Solution

  • From PHP7, the spaceship operator makes multiple comparisons very tidy.

    Declare your criteria as array eements in two balanced arrays -- the three-way operator will do the clever sorting for you.

    Code: (Demo) (New Demo)

    function sort3210ASC($a, $b) {
    if ($a3 !== $b3) return $a3 <=> $b3;
    if ($a3 !== $b3) return $a3 <=> $b3;
    if ($a1 !== $b1) return $a1 <=> $b1;
    if ($a[0] !== $b[0]) return $a[0] <=> $b[0];
    return 0;
    }

    function sort3210ASC($a, $b) {
        return [$a[3], $a[2], $a[1], $a[0]]
               <=>
               [$b[3], $b[2], $b[1], $b[0]];
    }
    
    usort($dud, 'sort3210ASC');
    var_export($dud);
    

    If i was to write a multisort approach, I would leverage array_column() instead of a foreach() loop to generate temporary columnar arrays. While the loop may be the microoptimized option, array_column() gives future code readers (humans) a more comprehensible snippet.

    Code: (Demo)

    array_multisort(
        array_column($dud, 3),
        array_column($dud, 2), 
        array_column($dud, 1), 
        array_column($dud, 0), 
        $dud
    );  
    var_export($dud);
    // sorts $dud by column 3 then 2 then 1 then 0