skip to Main Content

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…..

3

Answers


  1. 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
             /   |    
        (dd):(dd):(AM|PM)
           1     2     3
                  -->   
                 312
                 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) - .*$~',
        '312',
        $time
    );
    # ~> "PM0301"
    

    Now to actually sort the $props array, sort the array of all transformed $times and $props:

    $times = preg_replace(
        '~^(dd):(dd):(AM|PM) - .*$~',
        '312',
        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',
            ],
    ];
    

    Example on 3v4l.org.

    Login or Signup to reply.
  2. 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

    This variant sorts by the start of the time interval. If only the end is required you can use

    DateTime::createFromFormat('h:i:A - h:i:A',$a['time']); //same with $b
    
    Login or Signup to reply.
  3. 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);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search