1

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!!

  • 1
    You are performing 4 distinct and unrelated sorts which have no knowledge or previous sorts. Check out my post about using [`array_multisort()`](https://stackoverflow.com/q/51385519/2191572) or https://www.the-art-of-web.com/php/sortarray/#section_3 if you want to continue with `usort()` – MonkeyZeus Aug 03 '18 at 17:14
  • Thanks for your response, MonkeyZeus. I'll try your array_multisort() solution, see how it compares speedwise to the solution mickmackusa helped me find. Mickmackusa, sorry for the delay getting back to my post; I posted an answer to my own question here, but not sure about accepting an answer, besides commenting. I'll look into it. – Everett Staley Aug 06 '18 at 15:39
  • Hi MonkeyZeus, checking out your array_multisort info, adjusting my code to: foreach ($dud as $new => $sub) { $dud0[$new] = $sub[0]; $dud1[$new] = $sub[1]; $dud2[$new] = $sub[2]; $dud3[$new] = $sub[3]; } array_multisort($dud0,$dud1,$dud2,$dud3,$dud); unset($dud0,$dud1,$dud2,$dud3); is almost 4 times faster than the usort option. I was leaning toward usort because it doesn't create new arrays, but that much speed improvement, over temporary memory use for arrays that can be unset after use, seems the best choice. Thanks! – Everett Staley Aug 06 '18 at 16:16

2 Answers2

1

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
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
  • Of course, I don't have intimate knowledge of your project knowledge, but I think I would be sorting by `date` values before `time` values. If this data is being sourced from a database, then you should be sorting via the query -- because that will be cleaner. – mickmackusa Aug 03 '18 at 20:34
  • Thanks much, mickmackusa! This data didn't come straight from a query, I used a MWF or TThF availability table, to then create a schedule based on those availabilities, then match data from another table to fill appointments into the availability schedule I created. Your help is greatly appreciated!! – Everett Staley Aug 06 '18 at 15:33
  • It sounds like this all could be accomplished with a sibgle smart query, but that discussion is beyond the scope of this page. Happy coding. – mickmackusa Aug 06 '18 at 15:51
  • @Everett I've added my version of a multisort call in case you are still considering your options. – mickmackusa Aug 07 '18 at 03:04
0

Thanks mickmackusa, experimenting with your solution, I personalized it to:

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

Apparently doing four separate sorts, one of them altered a previous sort, even with the !== comparison operator, but your solution of putting them together in one sort did the trick. Thanks much!!

  • Does your php version not allow the spaceship operator? If you would like me to adjust my answer just let me know. I would appreciate it if you accepted my answer. – mickmackusa Aug 06 '18 at 15:36
  • I just accepted your answer. Spaceship operator works, I'm presuming one simple > comparison might be a tad quicker? Thanks again! – Everett Staley Aug 06 '18 at 15:44