简体   繁体   中英

Laravel sort collection and then by key

I am making raking system for my users and here's what I have so far:

Get all users and sort them by points - it works fine.

$users = User::all();
$users = $users->sortByDesc(function($item){
    return $item->points()->sum('amount');
});

Find your position in ranking - it works fine

$position = 0;
foreach($users as $user){
    if(Auth::user()->id == $user->id) break;
    $position++;
}

Get myself and users above/under me - it doesn't work. I get random users. It looks like the collection is not sorted anymore.

$myRank = new Collection();
if($position > 9){
    $myRank->add($users->get($position-1));
    $myRank->add($users->get($position));
    $myRank->add($users->get($position+1));
    return view('rank.show', ['topTen' => $users->take(15), 'myRank' => $myRank]);
}

Please help me with this one or give some hint on another approach(light weight for many records)

I think the problem is this:

When you call User::all() you get something like this:

0 => points: 10
1 => points: 50
2 => points: 30
3 => points: 70
4 => points: 20

Then you use the sortBy function, which reorder the collection, but does not reset the keys. So you end up with something like this:

3 => points: 70
1 => points: 50
2 => points: 30
4 => points: 20
0 => points: 10

So using position -1, position, and position +1 makes no sense here.

What you can do is using the values() function, which will reset the keys of you collection:

0 => points: 70
1 => points: 50
2 => points: 30
3 => points: 20
4 => points: 10

So I think the following code would work.

$users = User::all();
$users = $users->sortByDesc(function($item){
    return $item->points()->sum('amount');
})->values();

And then get 3 users from positions - 1 to position + 1:

$myRank = $users->splice($position - 1, 3);

To sort a collection by key, you can sort the backing array by key then recreate the collection again.

 $c = collect(['a' => 1, 'c' => 67, 'b' => 2]);
 $items = $c->all();
 ksort($items);

 $c = collect($items);

Or you can use a macro to get access to the backing array.

 Collection::macro('ksort', function(){
    //macros callbacks are bound to collection so we can safely access
    // protected Collection::items
    ksort($this->items);
    
    return $this;
    //to return a new instance
    //return collect($this->items);
 });

The last solution could be very useful if you are going to need to sort collections by key in many places in your code base

对于任何按key排序的array ,我建议使用原生 PHP function ksort()

You just need to use the sort collection method:

$c = collect(['a' => 1, 'c' => 67, 'b' => 2]);
$c->sort();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM