I have a 2D array named $props, the structure is as follows,
$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'],
];
I need to sort the array by 'time' key, to get the following result:
[
['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'],
];
I have found a solution with usort, the solution is as follows:
usort($props, function ($a, $b) {
return $a["time"] - $b["time"];
});
However, this is not working maybe because of special format of my time (but I will have to follow this specific time format.) and shows an error and do nothing to the array. The error:
Notice: A non well formed numeric value encountered in C:\xampp.....
CodePudding user response:
given the format of the date (03:01:PM - 04:50:PM
etc.) it needs to be made sort able, while keeping the original value.
from your question it could be seen that only the first part (03:01:PM
) is in use for sorting. even if not the case, lets keep it for the example (it can be easily extended).
given the C locale, transforming the time allows to get a string that can be just sorted (binary string order):
03:01:PM
/ | \
(\d\d):(\d\d):(AM|PM)
\1 \2 \3
-->
\3\1\2
PM0301
given a single $time
as input, the transformation can be done with a regular expression search and replace:
preg_replace(
'~^(\d ):(\d ):(AM|PM) - .*$~',
'\3\1\2',
$time
);
# ~> "PM0301"
Now to actually sort the $props
array, sort the array of all transformed $times
and $props
:
$times = preg_replace(
'~^(\d\d):(\d\d):(AM|PM) - .*$~',
'\3\1\2',
array_column($props, 'time')
);
array_multisort($times, $props);
Now $props
is sorted according to $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',
],
];
CodePudding user response:
This solution uses usort
with a special sorting function. Date objects are created using substr
and DateTime::createFromFormat
. The spaceship operator is use for comparison.
$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);
Try self: https://3v4l.org/Fq9U5
CodePudding user response:
For an approach with no regex and fewest iterated function calls, use array_map()
with iterated calls of createFromFormat()
, and use that generated sorting array as the first parameter of array_multisort()
to sort the input array.
Note that
in the createFromFormat()
method demands that all remaining characters are ignored.
Code: (Demo)
array_multisort(
array_map(
fn($row) => DateTime::createFromFormat('h:i:A ', $row['time']),
$props
),
$props
);
var_export($props);