如何通过 asc 和 desc 的多个属性对 Laravel 集合进行排序?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/47319120/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 16:58:22  来源:igfitidea点击:

How do I sort a Laravel Collection by multiple properties with both asc and desc?

phplaravelsortingcollections

提问by Tarek Adam

If I have an Illuminate\Support\Collection, how do I sort by multiple properties with both asc and desc? (This is a simple hypothetical - not at all looking for tips on query building.)

如果我有一个 Illuminate\Support\Collection,我如何使用 asc 和 desc 按多个属性排序?(这是一个简单的假设 - 根本不寻找有关查询构建的提示。)

$collection = User::all(); // not looking for User::orderBy()->get() solutions.  Read the question.
$sorting_insructions = [
    ['column'=>'first_name', 'order'=>'asc'],
    ['column'=>'date_of_birth', 'order'=>'desc'],
];
$collection->sort(function($a,$b) use ($sorting_instructions){
    // something...
});

采纳答案by Tarek Adam

Whoever uses this, just keep in mind - you'll need to tweak it depending if your using collection of objects or associative arrays. Should be an easy tweak. Just change the $a[]/$b[] stuff to $a-> and $b->

无论谁使用它,请记住 - 您需要根据您使用的对象集合还是关联数组来调整它。应该是一个简单的调整。只需将 $a[]/$b[] 内容更改为 $a-> 和 $b->

    public static function multiPropertySort(Collection $collection, array $sorting_instructions){

        return $collection->sort(function ($a, $b) use ($sorting_instructions){

            //stuff starts here to answer question...

            foreach($sorting_instructions as $sorting_instruction){

                $a[$sorting_instruction['column']] = (isset($a[$sorting_instruction['column']])) ? $a[$sorting_instruction['column']] : '';
                $b[$sorting_instruction['column']] = (isset($b[$sorting_instruction['column']])) ? $b[$sorting_instruction['column']] : '';

                if(empty($sorting_instruction['order']) or strtolower($sorting_instruction['order']) == 'asc'){
                    $x = ($a[$sorting_instruction['column']] <=> $b[$sorting_instruction['column']]);
                }else{
                    $x = ($b[$sorting_instruction['column']] <=> $a[$sorting_instruction['column']]);

                }

                if($x != 0){
                    return $x;
                }

            }

            return 0;

        })->values();
    }

回答by YouneL

If you are using Eloquent to get your collection instance, It would be much better to use orderBy methodin your query, especially if columns were indexed:

如果您正在使用 Eloquent 来获取您的集合实例,那么在您的查询中使用orderBy 方法会更好,特别是如果列被索引:

$sorting_insructions = [
    ['column'=>'first_name', 'order'=>'asc'],
    ['column'=>'date_of_birth', 'order'=>'desc'],
];

$collection = App\User::query();

foreach ($sorting_insructions as $value) {

    $collection->orderBy($value['column'], $value['order']);

}

$users = $collection->get();

EDITSince the question was edited telling that sort should be used outside the query builder, I think chaining sortByand sortByDescin reverse order from $sorting_insructionsgives the same result:

编辑由于编辑了问题,告诉排序应该在查询构建器之外使用,我认为以相反的顺序链接sortBysortByDesc$sorting_insructions给出相同的结果:

$collection = App\User::all();

$sorting_insructions = [
    ['column'=>'first_name', 'order'=>'asc'],
    ['column'=>'date_of_birth', 'order'=>'desc'],
];

for ($i = count($sorting_insructions) - 1; $i >= 0 ; $i--) { 

    extract($sorting_insructions[i]);

    if ( $order === 'asc') {
        $collection = $collection->sortBy( $column );
    } else {
        $collection = $collection->sortByDesc( $column );
    }

}

回答by Piterden

public static function multiPropertySort(
    Collection $collection,
    array $rules
)
{
    return $collection->sort(
        function ($a, $b) use ($rules) {
            foreach($rules as $rule){
                $sortColumn = array_get($rule, 'column');

                array_set(
                    $a,
                    $sortColumn,
                    array_get($a, $sortColumn, '')
                );

                array_set(
                    $b,
                    $sortColumn,
                    array_get($b, $sortColumn, '')
                );

                if ($sortOrder = array_get($rule, 'order', 'asc')) {
                    $x = (array_get($a, $sortColumn) <=> array_get($b, $sortColumn));
                } else {
                    $x = (array_get($b, $sortColumn) <=> array_get($a, $sortColumn));
                }

                if ($x != 0) {
                    return $x;
                }
            }

            return 0;
        }
    );
}

Let's imagine something imperative...

让我们想象一些势在必行的事情......

回答by NoOorZ24

It's a while after this question was asked and I'm not sure it's a best way to do this (because of many loops) but it's really short code.

在问这个问题之后过了一段时间,我不确定这是最好的方法(因为有很多循环),但它的代码真的很短。

$sortDrives = $drives->groupBy('date')->sortBy('date');
    $lastDrive = $sortDrives->map(function ($item, $key) {
    return $item->sortBy('odometer');
})->collapse()->last();

I pasted my own example as logic is simple enough to edit it

我粘贴了我自己的例子,因为逻辑很简单,可以编辑它

Explanation:

解释:

Lets say: date1 < date2and odometer1 < odometer2 < odometer3

让我们说:date1 < date2odometer1 < odometer2 < odometer3

At first you turn:

首先你转:

[
    0 => ['date' => date2, 'odometer' => odometer3],
    1 => ['date' => date1, 'odometer' => odometer2],
    2 => ['date' => date1, 'odometer' => odometer1],
]

Into:

进入:

[
    date2 => [
        0 => ['date' => date2, 'odometer' => odometer3]
    ],
    date1 => [
        0 => ['date' => date1, 'odometer' => odometer2],
        1 => ['date' => date1, 'odometer' => odometer1]
    ]
]

Then you sort it by new key and get:

然后您按新键对其进行排序并得到:

[
    date1 => ...
    date2 => ...
]

After that you sort by key #2 insideeach array date key is pointing at:

之后,你按#键2每个阵列日期键正指向:

[
    date1 => [
        0 => ['date' => date1, 'odometer' => odometer1],
        1 => ['date' => date1, 'odometer' => odometer2]
    ]
    date2 => [
        0 => ['date' => date2, 'odometer' => odometer3]
    ],
]

An finally you just collide all those arrays together (removing date key) and get:

最后,您只需将所有这些数组碰撞在一起(删除日期键)并得到:

[
    0 => ['date' => date1, 'odometer' => odometer1],
    1 => ['date' => date1, 'odometer' => odometer2],
    2 => ['date' => date2, 'odometer' => odometer3]
]