PHP通过包含Y-m-d H:i:s日期元素的元素对多维数组进行排序

142
我有一个数组,例如:
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
    )

)

有人能提供一种根据日期时间元素排序的方法吗?

11个回答

240

使用usort()和自定义比较函数:

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

编辑:你的数据是以数组形式组织的。为了更好地区分这些数据,我们将称内部数组(数据)为记录,这样你的数据就真的是一组记录的数组。

usort 函数每次会传递两个这样的记录到给定的比较函数 date_compare() 中。然后 date_compare 函数从每个记录中提取 "datetime" 字段作为 UNIX 时间戳(一个整数),并返回它们的差值。因此,如果两个日期相等,则结果将为 0;如果第一个参数 ($a) 大于第二个参数,则结果为正数,否则为负数 ($b)。 usort() 使用这些信息对数组进行排序。


1
在这个例子中,$array需要包含元素$a和$b吗?我得到了一个无效的比较函数错误。 - Tim
10
我不了解CodeIgniter,但你是否在类内部定义了该函数?那么你要么必须在类外面定义它,要么使用另一个回调参数:usort($array, array($this, "date_compare")),以便 usort() 知道它是一个类函数/方法。请参考:http://www.php.net/manual/en/language.pseudo-types.php#language.types.callback - Ferdinand Beyer
@Tim,你的问题得到解答了吗?我也在使用Codeigniter,并刚刚使用了@Ferdinand的答案来做你感兴趣的事情,如果仍有问题,我很乐意提供帮助。 - tim peterson
你也可以使用匿名函数来获得所需的结果。 - Bondt
这个对我返回一个布尔值,但是usort()并没有像你在答案底部所说的那样处理它。你知道为什么吗? - WasteD
显示剩余4条评论

60

这应该可以正常工作。我通过strtotime将日期转换为Unix时间。

  foreach ($originalArray as $key => $part) {
       $sort[$key] = strtotime($part['datetime']);
  }
  array_multisort($sort, SORT_DESC, $originalArray);
一种简洁的方法是使用多个数组方法:
array_multisort(array_map('strtotime',array_column($originalArray,'datetime')),
                SORT_DESC, 
                $originalArray);

1
这个很好用。只需将$originalArray替换为您的多维数组名称,并将'datetime'替换为日期子数组字段的名称即可。谢谢。 - washere
1
这个解决方案对我来说很好。排序选项也很棒(SORT_ASC或SORT_DESC)。非常感谢。 - Andras Barth
完美地运作,只有一个小问题(或者是我做错了什么)... 我需要按日期字段排序,该字段的日期格式为dd/mm/yyyy或yyyy/mm/dd(带斜杠)。在这种情况下,由于某种原因排序失败(仅按日排序,而不是整个日期)。我预处理了日期,将它们转换为dd-mm-yyyy或yyyy-mm-dd,然后它就可以工作了,谢谢!(为什么使用连字符可以工作,但使用斜杠不行?有什么想法吗?) - Javier Larroulet
array_multisort是唯一一个可以按照pubdate对我的json文件进行排序的方法。谢谢。 - grc

49

从php7开始,您可以使用太空船运算符

usort($array, function($a, $b) {
  return new DateTime($a['datetime']) <=> new DateTime($b['datetime']);
});

这将把数组转换为升序,如何转换为降序? - Nirav Bhoi
1
@Nirav 将 $a$b 交换(可以在自定义函数声明内部或 DateTime() 调用内部进行交换)。 - mickmackusa

8
请查看第三个示例:

http://us2.php.net/manual/zh/function.array-multisort.php

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

?>

建议使用Unix时间戳(自1970年以来的秒数)或MySQL时间戳(YmdHis-20100526014500),这对于解析器来说更容易,但在您的情况下我认为没有区别。


6

您可以使用带有回调函数的usort()轻松解决此问题。无需编写任何自定义功能。

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

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


6
大多数早期答案未能承认可用的比较日期时间值作为简单字符串的快捷方式。其他意识到不需要strtotime()的答案没有详细说明他们为什么这样做...所以我来解释一下。
因为你的datetime值是按降序排列的单位格式化的(YmdHis),并且每个单位都以相同数量的字符(4、2、2、2、2、2)一致地表示,并且分隔字符从一个字符串到另一个字符串都相同,所以你可以从左到右逐个字符地比较它们(自然而然)。对于格式为d/m/Y的日期字符串,情况并非如此。
有两个函数非常适合这个任务,我将展示ASC和DESC版本的两个函数。

演示链接

  • 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);
    
所有这些技术都是通过引用修改的,因此函数不提供真正有价值的返回值。
array_multisort():
- 不需要自定义函数 - 它确实需要某些循环机制来隔离日期时间列 - 它将失去数字键,但幸运的是它们对于这个问题并不重要 usort():
- 不使用排序方向常量,因此开发人员必须理解在太空船操作符的任一侧的 $a 在 $b 之前意味着 ASC,$b 在 $a 之前意味着 DESC - 需要一个自定义函数 - 可以通过调用 uasort() 来保留第一级键
对于那些确实需要解析日期或日期时间字符串以进行比较的人,因为其格式不允许即时字符串比较,这里是专门解释该任务注意事项的答案

使用array_multisort解决方案在PHP 7.0.7(?)以后会发出一个通知(“只应该返回/传递变量引用”)。为了解决这个问题,将array_column(...)移动到新变量中:array_multisort($dates,$array) - long
请创建一个3v4l.org演示以支持这个说法。我无法复制您的错误。https://3v4l.org/E6cCJ 也许是您的实现有问题。 - mickmackusa

5

按照指定的mysql日期时间字段和顺序排序记录/assoc_arrays数组:

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

2

我看到了这篇文章,但是当我返回类内的项目时想按时间排序,结果出现了错误。

于是我在php.net网站上进行了研究,并最终做出了以下操作:

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

您可以在PHP.net网站上找到非常有用的示例。

我的数组看起来像这样:

  '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)

2

对于那些仍在寻找解决办法的人,可以在类内部使用sortByDate函数来解决,参考下面的代码

<?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;

    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接