У меня есть 2D-массив с именем $props, структура которого выглядит следующим образом:
$props = [
['name' => 'Mathmatics', 'time' => '03:01:PM - 04:50:PM'],
['name' => 'History', 'time' => '11:30:AM - 01:30:PM'],
['name' => 'French', 'time' => '01:31:PM - 03:00:PM'],
];
Мне нужно отсортировать массив по ключу «время», чтобы получить следующий результат:
[
['name' => 'History', 'time' => '11:30:AM - 01:30:PM'],
['name' => 'French', 'time' => '01:31:PM - 03:00:PM'],
['name' => 'Mathmatics', 'time' => '03:01:PM - 04:50:PM'],
];
Я нашел решение с помощью usort, решение выглядит следующим образом:
usort($props, function ($a, $b) {
return $a["time"] - $b["time"];
});
Однако это не работает, возможно, из-за особого формата моего времени (но мне придется следовать этому конкретному формату времени) и показывает ошибку и ничего не делает с массивом. Ошибка:
Примечание. В C:\xampp..... обнаружено некорректное числовое значение.
«Однако это не работает, возможно, из-за особого формата моего времени» — тогда почему бы не настроить данные в этом usort
закрытии?
учитывая формат даты (03:01:PM - 04:50:PM
и т. д.), его необходимо сделать способным к сортировке, сохраняя при этом исходное значение.
из вашего вопроса видно, что для сортировки используется только первая часть (03:01:PM
). даже если это не так, давайте сохраним его для примера (его можно легко расширить).
учитывая локаль C, преобразование времени позволяет получить строку, которую можно просто отсортировать (двоичный порядок строк):
03:01:PM
/ | \
(\d\d):(\d\d):(AM|PM)
\1 \2 \3
-->
\3\1\2
PM0301
при наличии одного $time
в качестве входных данных преобразование можно выполнить с помощью поиска и замены регулярного выражения:
preg_replace(
'~^(\d+):(\d+):(AM|PM) - .*$~',
'\3\1\2',
$time
);
# ~> "PM0301"
Теперь, чтобы отсортировать массив $props
, отсортируйте массив всех преобразованных $times
и $props
:
$times = preg_replace(
'~^(\d\d):(\d\d):(AM|PM) - .*$~',
'\3\1\2',
array_column($props, 'time')
);
array_multisort($times, $props);
Теперь $props
сортируется по $times
:
[
[
'name' => 'History',
'time' => '11:30:AM - 01:30:PM',
],
[
'name' => 'French',
'time' => '01:31:PM - 03:00:PM',
],
[
'name' => 'Mathematics',
'time' => '03:01:PM - 04:50:PM',
],
];
В этом решении используется usort
со специальной функцией сортировки. Объекты даты создаются с использованием substr
и DateTime::createFromFormat
. Оператор космического корабля используется для сравнения.
$arr = [
['name' => 'Mathmatics', 'time' => '03:01:PM - 04:50:PM'],
['name' => 'History', 'time' => '11:30:AM - 01:30:PM'],
['name' => 'French', 'time' => '01:31:PM - 03:00:PM'],
];
usort($arr,function($a,$b){
$dta = DateTime::createFromFormat('h:i:A',substr($a['time'],0,8));
$dtb = DateTime::createFromFormat('h:i:A',substr($b['time'],0,8));
return $dta <=> $dtb;
});
print_r($arr);
Попробуйте сами: https://3v4l.org/Fq9U5
Этот вариант сортируется по началу временного интервала. Если требуется только конец, вы можете использовать
DateTime::createFromFormat('h:i:A - h:i:A',$a['time']); //same with $b
Анализ обоих выражений времени в диапазоне с помощью h:i:A - h:i:A
приводит к тому, что объект datetime учитывает только время окончания (что может быть не идеальным для всех случаев использования) 3v4l.org/FAFus
@mickmackusa: Да, либо по времени начала, либо по времени окончания. Я понятия не имею, как можно сделать сортировку, которая учитывает время начала и время окончания.
Для подхода без регулярных выражений и с наименьшим количеством повторных вызовов функций используйте array_map()
с повторными вызовами createFromFormat()
и используйте этот сгенерированный массив сортировки в качестве первого параметра array_multisort()
для сортировки входного массива.
Код: (Демо)
array_multisort(
array_map(
fn($row) => DateTime::createFromFormat('h:i:A+', $row['time']),
$props
),
$props
);
var_export($props);
Знак + в формате является хорошим решением, когда предупреждение в DateTime::getLastErrors() не является проблемой.
@jspit Я думаю, ты знаешь что-то, чего не знаю я. Пожалуйста, объясни.
возвращает "'предупреждения' => массив ( 8 => 'Конечные данные')". Совершенно излишне, если я указываю с помощью «+», что остальные данные следует игнорировать. 3v4l.org/cf9Wb
$a["time"] - $b["time"]
пытается вычесть строку с другой строкой, что по понятным причинам не сработает (и не имеет смысла). Если можете, добавьте время начала в виде отдельного ключа в лучшем формате (например,1130
,1331
,1501
и т. д.) и используйте его вместо этого. Что-то вродеreturn $a['sortTime'] <=> $b['sortTime'];