Массив PHP usort на основе элементов подмассива не работает правильно - глючит?

Вот мой текущий код, использующий php 7.1.20-1+ubuntu18.04.1+deb.sury.org+1: (Четыре столбца для сортировки, 5-й столбец просто номер подмассива.)

$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/>";
}

Я пытаюсь отсортировать 16 подмассивов, сначала по 4-му столбцу, затем по 3-му столбцу, затем по 2-му, затем по 1-му. Мой вывод:

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

Как есть, в выходных данных не по порядку подмассивы 3 и 5 (07:17 должно быть до 08:14), подмассивы 4 и 7 не по порядку (05:06 должно быть до 07:11) и подмассивы 8 и 14 вышли из строя (07:12 должно быть до 08:10). Комментируя различные строки usort, он сортирует четвертый столбец просто отлично, а все остальные строки usort закомментированы. Сортировка только столбцов 1 и 4 работает нормально. Сортировка только столбцов 2 и 4, подмассивы 3 и 5 не по порядку (07:17 должно быть раньше 8:14). Сортируем только столбцы 3 и 4, подмассивы 8 и 14 не по порядку (07:12 должно быть раньше 08:10). Есть идеи, что здесь происходит? Я пытался использовать информацию, доступную по адресу: PHP Sort Array By SubArray Value но в четвертой колонке все еще получаешь причудливую чудаковатую миссорт. Спасибо большое!!


person Everett Staley    schedule 03.08.2018    source источник
comment
Вы выполняете 4 различных и несвязанных сортировки, которые не имеют знаний или предыдущих сортировок. Посмотрите мой пост об использовании array_multisort() или the-art-of-web.com/php/sortarray/#section_3, если вы хотите продолжить с usort()   -  person MonkeyZeus    schedule 03.08.2018
comment
Спасибо за ответ, MonkeyZeus. Я попробую ваше решение array_multisort(), посмотрю, как оно по скорости сравнивается с решением, которое мне помог найти Микмакуса. Mickmackusa, извините за задержку с возвращением к моему сообщению; Я разместил здесь ответ на свой вопрос, но не уверен, что приму ответ, кроме комментариев. Я посмотрю на это.   -  person Everett Staley    schedule 06.08.2018
comment
Привет, MonkeyZeus, проверяю вашу информацию array_multisort, корректирую свой код следующим образом: 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); почти в 4 раза быстрее, чем вариант usort. Я склонялся к usort, потому что он не создает новые массивы, но такое значительное улучшение скорости по сравнению с использованием временной памяти для массивов, которые можно сбросить после использования, кажется лучшим выбором. Спасибо!   -  person Everett Staley    schedule 06.08.2018


Ответы (2)


Начиная с PHP7, оператор космического корабля делает множественные сравнения очень аккуратными.

Объявите свои критерии как элементы массива в двух сбалансированных массивах — трехсторонний оператор сделает умную сортировку за вас.

Код: (Демо) (Новая демонстрация)

function sort3210ASC($a, $b) {
if ($a3 !== $b3) возвращает $a3 ‹=> $b3;
if ($a 3 !== $b3) возвращает $a3 ‹=> $b3;
if ($a1 !== $b1) возвращает $a1 ‹=> $b1;
if ($a[0] !== $b[0]) вернуть $a[0] ‹=> $b[0];
вернуть 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);

Если бы мне нужно было написать мультисортный подход, я бы использовал array_column() вместо цикла foreach() для создания временных столбчатых массивов. Хотя цикл может быть микрооптимизированным вариантом, array_column() дает будущим читателям кода (людям) более понятный фрагмент.

Код: (Демо)

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
person mickmackusa    schedule 03.08.2018
comment
Конечно, у меня нет глубоких знаний о вашем проекте, но я думаю, что я бы сортировал по значениям date перед значениями time. Если эти данные берутся из базы данных, то вы должны сортировать их с помощью запроса, потому что это будет чище. - person mickmackusa; 03.08.2018
comment
Большое спасибо, микмакуса! Эти данные не были получены прямо из запроса, я использовал таблицу доступности MWF или TThF, чтобы затем создать расписание на основе этих возможностей, а затем сопоставить данные из другой таблицы, чтобы заполнить встречи в созданном мной расписании доступности. Ваша помощь очень ценится!! - person Everett Staley; 06.08.2018
comment
Похоже, все это можно было бы выполнить с помощью умного запроса sibgle, но это обсуждение выходит за рамки этой страницы. Удачного кодирования. - person mickmackusa; 06.08.2018
comment
@Everett Я добавил свою версию мультисортного вызова на случай, если вы все еще рассматриваете свои варианты. - person mickmackusa; 07.08.2018

Спасибо, micmackusa, поэкспериментировав с вашим решением, я персонализировал его для:

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;
});

По-видимому, выполняя четыре отдельные сортировки, одна из них изменила предыдущую сортировку, даже с оператором сравнения !==, но ваше решение объединить их в одну сортировку помогло. Спасибо большое!!

person Everett Staley    schedule 06.08.2018
comment
Ваша версия php не позволяет оператору космического корабля? Если вы хотите, чтобы я скорректировал свой ответ, просто дайте мне знать. Буду признателен, если вы примете мой ответ. - person mickmackusa; 06.08.2018
comment
Я только что принял твой ответ. Оператор космического корабля работает, я предполагаю, что одно простое › сравнение может быть немного быстрее? Спасибо еще раз! - person Everett Staley; 06.08.2018