按照一个属性对对象数组进行排序

662

如何根据对象数组中的某个字段(例如 namecount)进行排序?

Array
(
    [0] => stdClass Object
        (
            [ID] => 1
            [name] => Mary Jane
            [count] => 420
        )

    [1] => stdClass Object
        (
            [ID] => 2
            [name] => Johnny
            [count] => 234
        )

    [2] => stdClass Object
        (
            [ID] => 3
            [name] => Kathy
            [count] => 4354
        )

   ....

1
countname变量/动态的吗?还是开发人员静态地“知道”它们? - mickmackusa
23个回答

932

使用usort,这里是从手册中改编的示例:

function cmp($a, $b) {
    return strcmp($a->name, $b->name);
}

usort($your_data, "cmp");

第二个参数也可以使用任何可调用对象。以下是一些示例:

  usort($your_data, function($a, $b) {return strcmp($a->name, $b->name);});
从类内部
  usort($your_data, array($this, "cmp")); // "cmp" should be a method in the class
  • 使用箭头函数(自PHP 7.4起)

  •   usort($your_data, fn($a, $b) => strcmp($a->name, $b->name));
    

    另外,如果你在比较数值时,可以使用fn($a, $b) => $a->count - $b->count作为“compare”函数来实现,或者如果你想用另一种方式做同样的事情,从PHP 7开始,你可以使用Spaceship operator,像这样:fn($a, $b) => $a->count <=> $b->count


    101
    这很棒,但如果排序函数与调用函数在同一个类中,您应该使用: usort($your_data, array($this, "cmp")); - rmooney
    7
    是的,但只有在你在一个类内部时才能这样做。 - cambraca
    12
    将@rmooney在stackoverflow上的第一条评论放入你的答案中。 - Mohammad Faisal
    7
    如果您的比较函数在您要比较的模型/对象中(在我看来这是更清晰的设计),则必须包含完整的命名空间到您的模型/对象,如下所示:uasort($members, array("Path\to\your\Model\Member", "compareByName")); - clauziere
    3
    一句话概括:使用usort函数对$your_data进行排序,排序方式为按照对象的name属性进行字符串比较。注解:这行代码使用了PHP语言的usort函数,其中第一个参数为待排序的数据($your_data),第二个参数是自定义的比较函数。该比较函数通过strcmp函数对两个对象的name属性进行字符串比较,并返回比较结果来判断它们之间的大小关系,从而实现排序。 - Luís Henriques
    显示剩余7条评论

    524

    这里有一个使用闭包的更好的方法

    usort($your_data, function($a, $b)
    {
        return strcmp($a->name, $b->name);
    });
    
    请注意,这并不在 PHP 的文档中,但如果你使用的是5.3+版本,就支持闭包函数(closures),可以提供可调用参数(callable arguments)。

    19
    我喜欢这个答案胜过被采纳的答案,因为我们可以快速定义比较函数,并且可以在类中使用。 - Nam G VU
    12
    如需保留数组键,请使用 uasort() - gillytech
    10
    按照降序排列,使用-1 * strcmp($a->name, $b->name); - Wallace Vizerra
    22
    无需使用乘法来进行降序排序,只需交换参数:strcmp($b->name, $a->name) - zxcat
    3
    你可能会遇到和我一样的情况,即接受的答案比这个更可取。例如,你可能有一个父类和一个子类。子类重写了一个使用 usort 的函数,但是比较函数是相同的。如果使用这种方法,你需要复制闭包的代码,而不是调用父类中只需要定义一次的 protected static 方法。 - Pere
    显示剩余7条评论

    84

    如果您想对整数值进行排序:

    // Desc sort
    usort($array,function($first,$second){
        return $first->number < $second->number;
    });
    
    // Asc sort
    usort($array,function($first,$second){
        return $first->number > $second->number;
    });
    

    更新:在字符串中不要忘记转换为相同的大小写形式(大写或小写)

    // Desc sort
    usort($array,function($first,$second){
        return strtolower($first->text) < strtolower($second->text);
    });
    
    // Asc sort
    usort($array,function($first,$second){
        return strtolower($first->text) > strtolower($second->text);
    });
    

    对我来说,$first->number 没有起作用。我需要使用 $first["number"] 代替。 - Androidz
    你可以使用 PHP 原生的 strcasecmp() 函数来比较字符串并忽略大小写,而不是使用 strtolower()。请参阅 PHP: strcasecmp - Manual - Zoup

    46

    如果您正在使用 PHP 面向对象编程,您可能需要更改为:

    public static function cmp($a, $b) 
    {
        return strcmp($a->name, $b->name);
    }
    
    //in this case FUNCTION_NAME would be cmp
    usort($your_data, array('YOUR_CLASS_NAME','FUNCTION_NAME')); 
    

    35
    usort($array, 'my_sort_function');
    
    var_dump($array);
    
    function my_sort_function($a, $b)
    {
        return $a->name < $b->name;
    }
    

    相同的代码将与count字段一起使用。

    关于usort的更多详细信息:https://www.php.net/usort

    顺便问一下,你从哪里获取到那个数组的?希望不是从数据库里取的吧?


    2
    @cambraca:哦,我忘了它可以通过引用接受数组。顺便说一下,OP没有说明他需要按哪个顺序对集合进行排序。 - zerkms
    3
    为什么不在数据库中排序呢?使用 ORDER BY count - zerkms
    1
    @Piohen:"你在Oracle表中有几百万行数据(没有索引),你需要取出前十条并排序" --- 你是否意识到,如果没有索引,“取最大的十条”任务会导致全表扫描和内存排序或“数百万行数据”的处理? - zerkms
    1
    @zerkms:我认为有一个误解。我只是想指出,在SQL查询中盲目使用“order by”不是一个好主意。有时在应用程序中执行内存排序更好:PHP、Java等,甚至有时候JavaScript也可以。因此,在我看来,“总是使用ORDER BY,永远不要在应用程序中排序”是错误的建议。 - Piohen
    1
    @zerkms:我之前只是留了几个评论;-) 如果你想真正体验一下,为什么不自己复制它并测量时间呢?从数百万行中获取10个已排序的行肯定比从数百万行中获取同样10个未排序的行要慢。一些关系型数据库管理系统始终使用磁盘空间和临时表进行排序,这就是为什么在你的应用程序中使用内存排序甚至使用冒泡排序也会更快地对这10行进行排序的原因。如果你使用JavaScript进行排序,你根本不需要承担排序负担。关系型数据库管理系统并不总是比你的应用程序更快。至此我的发言结束。 - Piohen
    显示剩余10条评论

    11

    如果所有方法都失败了,这里是另一种解决方案:

    $names = array(); 
    foreach ($my_array as $my_object) {
        $names[] = $my_object->name; //any object field
    }
    
    array_multisort($names, SORT_ASC, $my_array);
    
    return $my_array;
    

    1
    SORT_ASC 是默认排序方向,可以安全地省略。 - mickmackusa
    1
    它有效了,其他的对我都没用。 - Waseem Sarwar

    10

    您可以使用此函数(适用于PHP版本>=5.3):

    function sortArrayByKey(&$array,$key,$string = false,$asc = true){
        if($string){
            usort($array,function ($a, $b) use(&$key,&$asc)
            {
                if($asc)    return strcmp(strtolower($a{$key}), strtolower($b{$key}));
                else        return strcmp(strtolower($b{$key}), strtolower($a{$key}));
            });
        }else{
            usort($array,function ($a, $b) use(&$key,&$asc)
            {
                if($a[$key] == $b{$key}){return 0;}
                if($asc) return ($a{$key} < $b{$key}) ? -1 : 1;
                else     return ($a{$key} > $b{$key}) ? -1 : 1;
    
            });
        }
    }
    

    例子:

    sortArrayByKey($yourArray,"name",true); //String sort (ascending order)
    sortArrayByKey($yourArray,"name",true,false); //String sort (descending order)
    sortArrayByKey($yourArray,"id"); //number sort (ascending order)
    sortArrayByKey($yourArray,"count",false,false); //number sort (descending order)
    

    我使用了$a->{$key}$b->{$key}而不是$a[$key]$b[$key],因为严格来说我们处理的是属性而不是数组成员,但这仍然是我要找的答案。 - SteJ
    请在示例代码中实现@SteJ的建议,因为您提供的解决方案仅适用于简单对象,但使用SteJ的修复程序可以适用于我尝试过的所有对象数组。 - user2993145

    10

    要按一个值列进行排序,结合使用array_column()array_multisort()是一个明智的方式。示例

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

    或者只需使用太空船运算符调用usort(),以在此场景中执行更少的迭代。 演示

    usort($array, fn($a, $b) => $a->count <=> $b->count);
    

    请注意,虽然输入数组中的计数值被转换为字符串类型值,但两个排序函数都将正确地按数字大小对值进行排序,而不是将它们字母表顺序排序(错误地将23420放在420 之前)。这是一个可靠的默认功能。
    即使您正在可变地声明要进行排序的列,两种方法都允许使用该变量而无需任何其他技术。 带有变量的多重排序演示
    $property = 'count';
    array_multisort(array_column($array, $property), $array);
    

    带变量的Usort演示

    $property = 'count';
    usort($array, fn($a, $b) => $a->$property <=> $b->$property);
    

    两种原生排序函数都是通过引用修改数组,因此不要尝试通过它们的返回值访问已排序的数组。

    array_multisort() 的默认排序方向是升序,因此在两个数组参数之间显式使用 SORT_ASC 没有任何好处。如果希望进行降序排序,请在两个数组之间写入 SORT_DESC(作为第二个参数)。

    usort() 在自定义函数将 $a 数据放在太空船运算符的左侧,$b 数据放在右侧时进行升序排序。要进行降序排序,只需将 $b 数据放在左侧,$a 数据放在右侧。

    这两种方法都能够接收多个排序规则,但由于这个问题只要求在单个列上进行排序,因此这个指导在这里不适用。

    在排序时,在每次迭代中调用一个函数(如 strcmp())会更加低效。这不再是最佳实践。使用双向比较(如 ><)返回布尔结果也不再是最佳实践。在 usort() 中需要三向比较。

    对于具有多个规则/列/属性的数据排序,这个答案 给出了很好的指导。


    6
    您可以使用usort进行排序,如下所示:
    usort($array,function($first,$second){
        return strcmp($first->name, $second->name);
    });
    

    5

    如果您想要对日期进行排序

       usort($threads,function($first,$second){
            return strtotime($first->dateandtime) < strtotime($second->dateandtime);
        });
    

    这是对另一个问题的回答。原始问题并不涉及日期表达式的排序。Stack Overflow有其他专门用于此任务的排序页面。 - mickmackusa

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