【问题标题】:Why usort reverts an array when values are equals on PHP prior to 7.0为什么在 7.0 之前的 PHP 上值相等时,usort 会还原数组
【发布时间】:2020-05-25 23:53:48
【问题描述】:

我正在尝试在数组上使用 usort,并获得关于 PHP 版本的不同行为:

$arr1 = [
    [
        'points' => 10,
        'date' => 3,
    ],
    [
        'points' => 10,
        'date' => 2,
    ],
    [
        'points' => 10,
        'date' => 1,
    ]
];

usort($arr1, function ($a, $b) {
    if($a['points'] == $b['points']){
      return 0;
    }
    return ($a['points'] < $b['points']) ? 1 : -1;
});

此数组包含一个“日期”值,并按此值排序 DESC,根据“点”值进行排序后,具有相同“点”值的项目在 PHP 5.6 上反转,并保持其原始顺序在 PHP 7.0...

您可以在此处使用不同的 PHP 版本进行在线测试(请针对 PHP 5.6.29 和 7.0.1 进行测试):http://sandbox.onlinephpfunctions.com/code/2715220539623b4b699ebb3f90a4b01c98eef53d

在任何 PHP 版本下,我怎样才能在相等的点上获得相同的排序行为?

【问题讨论】:

  • 你想要哪种行为? PHP 5.6 或 7 中的那个?
  • PHP 的排序函数并不稳定,它们没有指定相等元素的顺序。
  • 这称为未定义行为。如果两个项目相同,则不应依赖它们的顺序。

标签: php usort


【解决方案1】:

正如其他人已经指出的那样,您需要对多个键进行排序。 PHP 通过其 compare_func 可调用函数提供了这一点,它还提供了 &lt;=&gt; *("spaceship" ) 运算符,以方便您编写此类函数。

如果您的compare_func 发现points 相等,它应该继续比较date 等等,返回第一个不为零的值。 (您可以使用or 运算符来执行此操作。)

而且,如果 所有 的排序键都相等,usort() 的行为是“不可预测的”。在这种情况下,您不应假定记录将以任何特定顺序显示……即使从对usort() 的一次调用到下一次调用也是如此。如果您认为“这个版本的 PHP '总是' 这样做,而那个版本 '总是' 以其他方式这样做”,那么假设你只是错了。

【讨论】:

    【解决方案2】:
        $array = [
            ['points' => 10, 'date' => 3],
            ['points' => 10, 'date' => 2],
            ['points' => 10, 'date' => 1],
        ];
    
        usort($array, function ($a, $b) {
            if($a['points'] === $b['points']){
                return $b['date'] <=> $a['date'];
            }
            return $a['points'] <=> $b['points'];
        });
    
        var_dump($array);
    

    【讨论】:

    • 很遗憾,&lt;=&gt; 运算符只有 PHP 7+,所以会在 PHP 5 中产生语法错误。
    • 尽管如此,这里的基本思想是正确的:您需要对多个键进行排序。当两个值具有相同的points 值时,比较date .此外:如果两条记录中的所有排序键值都相同,您应该认为最终的排序只是“不可预测”。
    • 是的,你是对的@NigelRen。我正在为 PHP 7+ 版本提供解决方案。我想知道我们现在怎么还谈论 PHP 版本 5...无论如何,我们都知道可以使用三元运算符而不是 &lt;=&gt; :)
    【解决方案3】:

    如果您想在两个版本中获得相同的结果,则需要确保指定相等项如何处理其他字段。

    所以你可以用...解决这个问题

    usort($arr1, function ($a, $b) {
        if($a['points'] == $b['points']){
          return $a['date'] - $b['date'];
        }
        return ($a['points'] < $b['points']) ? 1 : -1;
    });
    

    因此,如果 points 的值相同,则使用 date 作为微分器(假设可以进行数字比较。)

    【讨论】:

      猜你喜欢
      • 2017-06-10
      • 1970-01-01
      • 2022-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多