PHP Sort a multidimensional array by element containing Y-m-d H:i:s date
Asked Answered
B

11

142

I have an array such as:

Array
(
[0] => Array
    (
        [id] => 2
        [type] => comment
        [text] => hey
        [datetime] => 2010-05-15 11:29:45
    )

[1] => Array
    (
        [id] => 3
        [type] => status
        [text] => oi
        [datetime] => 2010-05-26 15:59:53
    )

[2] => Array
    (
        [id] => 4
        [type] => status
        [text] => yeww
        [datetime] => 2010-05-26 16:04:24
    )

)

Can anyone suggest a way to sort/order this based on the datetime element?

Busily answered 26/5, 2010 at 6:39 Comment(0)
D
240

Use usort() and a custom comparison function:

function date_compare($a, $b)
{
    $t1 = strtotime($a['datetime']);
    $t2 = strtotime($b['datetime']);
    return $t1 - $t2;
}    
usort($array, 'date_compare');

EDIT: Your data is organized in an array of arrays. To better distinguish those, let's call the inner arrays (data) records, so that your data really is an array of records.

usort will pass two of these records to the given comparison function date_compare() at a a time. date_compare then extracts the "datetime" field of each record as a UNIX timestamp (an integer), and returns the difference, so that the result will be 0 if both dates are equal, a positive number if the first one ($a) is larger or a negative value if the second argument ($b) is larger. usort() uses this information to sort the array.

Docent answered 26/5, 2010 at 6:44 Comment(9)
in this example, does $array need to contain elements $a and $b? - im getting an error invalid compare functionBusily
Nope, $a and $b will hold the arrays within $array. Make sure that you did not misspell the function name.Docent
im not sure I understand. In my example, I have 3 arrays in the array - I have confirmed the name, still getting invalid comparison functionBusily
See the explanation I added. I tested the code and it works fine for me!Docent
Thanks, your explanation made sense.Im still getting the error. Im using Codeigniter framework and have added the date_compare function to the controller. ive checked the spelling, tried alternate spelling, I tried just returning 0 from the function, still getting the errorBusily
I don't know CodeIgniter, but are you defining the function inside a class? Than you either have to define it outside the class or use a different callback argument: usort($array, array($this, "date_compare")), so that usort() knows it's a class function/method. See also: php.net/manual/en/…Docent
@Tim, did your question get answered? I'm using Codeigniter too, and just used -@Ferdinand's answer to do exactly what you're interested so I'm happy to help if there is still an issue.Ambler
You can also use an anonymous function to get the desired result.Pugliese
This return a boolean for me but usort() doesn't handlet it like you said at the bottom of your answer. Do you know why?Cloying
E
61

This should work. I converted the date to unix time via strtotime.

  foreach ($originalArray as $key => $part) {
       $sort[$key] = strtotime($part['datetime']);
  }
  array_multisort($sort, SORT_DESC, $originalArray);

One-liner version would be using multiple array methods:

array_multisort(array_map('strtotime',array_column($originalArray,'datetime')),
                SORT_DESC, 
                $originalArray);
Ecclesiastical answered 17/12, 2013 at 20:0 Comment(4)
This works great. Just replace: $originalArray with name of your multi-dimensional array. And replace: 'datetime' with the name of your sub-array field for date. Thanks.Stook
this solution is doing well for me. the sort option is great too (SORT_ASC or SORT_DESC). Thanks a lot.Softspoken
works beautifully with one small caveat (either that, or I'm doing something wrong)... the date field I need to sort by has dates in the format dd/mm/yyyy or yyyy/mm/dd (with slashes). In that case, the sorting didn't work for some reason (sorted only by the day, not the whole date). I pre-processed dates to convert them into either dd-mm-yyyy or yyyy-mm-dd and it worked so thanks!!! (any ideas on why it works with hyphens but not slashes?)Receiver
array_multisort is the only sort method that worked to sort my json file by pubdate. thanks.Ladyinwaiting
T
49

From php7 you can use the Spaceship operator:

usort($array, function($a, $b) {
  return new DateTime($a['datetime']) <=> new DateTime($b['datetime']);
});
Turaco answered 7/11, 2016 at 10:34 Comment(2)
This will transform array in ascending how to transform in descending?Immortalize
@Nirav swap the $a with the $b (either inside the custom function declaration or inside of the DateTime() calls.Nievesniflheim
T
8

https://www.php.net/manual/en/function.array-multisort.php see third example:

<?php

$data[] = array('volume' => 67, 'edition' => 2);
$data[] = array('volume' => 86, 'edition' => 1);
$data[] = array('volume' => 85, 'edition' => 6);
$data[] = array('volume' => 98, 'edition' => 2);
$data[] = array('volume' => 86, 'edition' => 6);
$data[] = array('volume' => 67, 'edition' => 7);

foreach ($data as $key => $row) {
    $volume[$key]  = $row['volume'];
    $edition[$key] = $row['edition'];
}

array_multisort($volume, SORT_DESC, $edition, SORT_ASC, $data);

?>

fyi, using a unix (seconds from 1970) or mysql timestamp (YmdHis - 20100526014500) would be be easier for the parser but i think in your case it makes no difference.

Turban answered 26/5, 2010 at 6:46 Comment(0)
N
7

Most of the earlier answers failed to acknowledge the available shortcut of comparing the datetime values as simple strings. Other answers that realized that strtotime() was unnecessary did not spell out why did what they did ...so I will.

Because your datetime values are formatted with units in descending size (Y, m, d, H, i, s) AND because each unit is consistently expressed with the same number of characters (4, 2, 2, 2, 2, 2) AND because the delimiting characters are all identical from string to string, you can simply compare them character-by-character from left to right (naturally). This would not be true for a date string formatted as, say, d/m/Y.

There are two functions that are ideal for this task, I'll demonstrate ASC and DESC versions for both.

Demo Link

  • usort() by date column DESC:

    usort($array, function($a, $b) { return $b['datetime'] <=> $a['datetime']; });
    
  • usort() by date column ASC:

    usort($array, function($a, $b) { return $a['datetime'] <=> $b['datetime']; });
    
  • usort() by date column ASC in >= PHP7.4:

    usort($array, fn($a, $b) => $a['datetime'] <=> $b['datetime']);
    
  • array_multisort() by date column DESC:

     array_multisort(array_column($array, 'datetime'), SORT_DESC, $array);
    
  • array_multisort() by date column ASC:

    array_multisort(array_column($array, 'datetime'), $array);
    

All of these techniques modify by reference, so the function provide no real valuable return value.

array_multisort():

  • doesn't need a custom function
  • it does need the datetime column to be isolated by some looping mechanism
  • it will lose numeric keys, but fortunately they are not valuable for this question

usort():

  • doesn't use sorting direction constants, so developers must understand that $a before $b (on either side of the spaceship operator) means ASC and $b before $a means DESC
  • requires a custom function
  • can be adjusted to preserve first level keys by calling uasort() instead

For anyone who truly DOES need a date or datetime string to be parsed because its format does not permit instant string comparisons, here is an answer devoted to explaining the caveats of that task.

Nievesniflheim answered 16/4, 2021 at 11:36 Comment(2)
Solution with array_multisort emits a notice ("only variable references should be returned/passed by reference") since PHP 7.0.7 (?). To fix this, move array_column(...) to new variable: array_multisort($dates, $array).Sufflate
Please create a 3v4l.org demo to support this claim. I cannot replicate your error. 3v4l.org/E6cCJ Perhaps there is something wrong with your implementation.Nievesniflheim
U
6

$array = Array
(
  [0] => Array
   (
    [id] => 2
    [type] => comment
    [text] => hey
    [datetime] => 2010-05-15 11:29:45
   )

 [1] => Array
  (
    [id] => 3
    [type] => status
    [text] => oi
    [datetime] => 2010-05-26 15:59:53
  )

  [2] => Array
   (
    [id] => 4
    [type] => status
    [text] => yeww
    [datetime] => 2010-05-26 16:04:24
   )

   );
   print_r($array);   
   $name = 'datetime';
   usort($array, function ($a, $b) use(&$name){
      return $a[$name] - $b[$name];});

   print_r($array);
Unison answered 11/12, 2014 at 11:58 Comment(0)
G
6

You can simply solve this problem using usort() with callback function. No need to write any custom function.

$your_date_field_name = 'datetime';
usort($your_given_array_name, function ($a, $b) use (&$your_date_field_name) {
    return strtotime($a[$your_date_field_name]) - strtotime($b[$your_date_field_name]);
});
Gown answered 7/10, 2016 at 11:1 Comment(0)
C
5

Sorting array of records/assoc_arrays by specified mysql datetime field and by order:

function build_sorter($key, $dir='ASC') {
    return function ($a, $b) use ($key, $dir) {
        $t1 = strtotime(is_array($a) ? $a[$key] : $a->$key);
        $t2 = strtotime(is_array($b) ? $b[$key] : $b->$key);
        if ($t1 == $t2) return 0;
        return (strtoupper($dir) == 'ASC' ? ($t1 < $t2) : ($t1 > $t2)) ? -1 : 1;
    };
}


// $sort - key or property name 
// $dir - ASC/DESC sort order or empty
usort($arr, build_sorter($sort, $dir));
Curlicue answered 20/11, 2013 at 12:3 Comment(0)
K
2

I came across to this post but I wanted to sort by time when returning the items inside my class and I got an error.

So I research the php.net website and end up doing this:

class MyClass {
   public function getItems(){
      usort( $this->items, array("MyClass", "sortByTime") );
      return $this->items;
   }
   public function sortByTime($a, $b){
      return $b["time"] - $a["time"];
   }
}

You can find very useful examples in the PHP.net website

My array looked like this:

  'recent' => 
    array
      92 => 
        array
          'id' => string '92' (length=2)
          'quantity' => string '1' (length=1)
          'time' => string '1396514041' (length=10)
      52 => 
        array
          'id' => string '52' (length=2)
          'quantity' => string '8' (length=1)
          'time' => string '1396514838' (length=10)
      22 => 
        array
          'id' => string '22' (length=2)
          'quantity' => string '1' (length=1)
          'time' => string '1396514871' (length=10)
      81 => 
        array
          'id' => string '81' (length=2)
          'quantity' => string '2' (length=1)
          'time' => string '1396514988' (length=10)
Kithara answered 3/4, 2014 at 9:8 Comment(0)
E
2

For those still looking a solved it this way inside a class with a function sortByDate, see the code below

<?php

class ContactsController 
{
    public function __construct()
    {
    //
    }


    function sortByDate($key)
    {
        return function ($a, $b) use ($key) {
            $t1 = strtotime($a[$key]);
            $t2 = strtotime($b[$key]);
            return $t2-$t1;
        };

    }

    public function index()
    {

        $data[] = array('contact' => '434343434', 'name' => 'dickson','updated_at' =>'2020-06-11 12:38:23','created_at' =>'2020-06-11 12:38:23');
        $data[] = array('contact' => '434343434', 'name' => 'dickson','updated_at' =>'2020-06-16 12:38:23','created_at' =>'2020-06-10 12:38:23');
        $data[] = array('contact' => '434343434', 'name' => 'dickson','updated_at' =>'2020-06-7 12:38:23','created_at' =>'2020-06-9 12:38:23');


        usort($data, $this->sortByDate('updated_at'));

        //usort($data, $this->sortByDate('created_at'));
        echo $data;

    }
}
Elfish answered 11/6, 2020 at 10:10 Comment(0)
E
1

For 'd/m/Y' dates:

usort($array, function ($a, $b, $i = 'datetime') { 
    $t1 = strtotime(str_replace('/', '-', $a[$i]));
    $t2 = strtotime(str_replace('/', '-', $b[$i]));

    return $t1 > $t2;
});

where $i is the array index

Eugenides answered 3/10, 2018 at 20:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.