Shaun Moore
Shaun Moore

Reputation: 122

Points ranking in Laravel skip over equal points

I'm trying to create a list of users on a leaderboard.

In my case I have users with the same amount of points, and with this I would like to skip over the next x amount of ranks see below:

i.e

Position | Points
1           100
2            50
2            50
3            30
4            20
4            20
6            10

I've looked almost everywhere at an example of this and the closet I could find was this SO answer But they seem to have done half the job where they don't display the second (2) position or the second 5th and I need to show all the positions.

Here is my code (I tried removing values() like the other answer but it just makes the $key into the points value)

$ranks = $bets->groupBy('user_id')
                ->transform(function ($userGroup) {
                    // Set initial points value.
                    $points = 0;

                    // Map over the user group.
                    $userGroup->map(function ($user) use (&$points) {
                        // Assign points.
                        $points = $points + $user->points;
                    });

                    // Set the first users points format.
                    $userGroup->first()->user->points = number_format((float) $points, 2, '.', '');

                    // Return the first user.
                    return $userGroup->first()->user;
                })
                ->sortByDesc('points')->groupBy('points')
                ->values()
                ->transform(function ($userGroup, $key) {
                    // Return the transformed usergroup.
                    return $userGroup->transform(function ($user) use ($key) {
                        // Set the user's position.
                        $user->position = $key + 1;
                        // Return the user.
                        return $user;
                    });
                })

Current output

collection
 array  
   0 => usercollection
     0 => usercollection (position = 1)
   1 => usercollection
     0 => usercollection (position = 2)
     1 => usercollection (position = 2)
   2 => usercollection 
     0 => usercollection (position = 3)
   3 => usercollection 
     0 => usercollection (position = 4)
     1 => usercollection (position = 4)
   4 => usercollection 
     0 => usercollection (position = 5)

Expected outcome

collection
 array  
   0 => usercollection
     0 => usercollection (position = 1)
   1 => usercollection
     0 => usercollection (position = 2)
     1 => usercollection (position = 2)
   2 => usercollection 
     0 => usercollection (position = 4)
   3 => usercollection 
     0 => usercollection (position = 5)
     1 => usercollection (position = 5)
   4 => usercollection 
     0 => usercollection (position = 6)

Upvotes: 0

Views: 188

Answers (1)

Dozz
Dozz

Reputation: 46

You can use the chunkWhile function like this

$collection = collect([
        ['position' => 1, 'point' => 100,],
        ['position' => 2, 'point' => 90,],
        ['position' => 2, 'point' => 90,],
        ['position' => 3, 'point' => 80,],
        ['position' => 4, 'point' => 70,],
        ['position' => 4, 'point' => 70,],
        ['position' => 5, 'point' => 60,],
    ]);

    $collection = $collection->chunkWhile(function ($item, $key, $chunk){
        return $item['point'] === $chunk->last()['point'];
    });

Result :

collect(
        collect(
            ['position' => 1, 'point' => 100,],
        ),
        collect(
            ['position' => 2, 'point' => 90,],
            ['position' => 2, 'point' => 90,],
        ),
        collect(
            ['position' => 3, 'point' => 80,],
        ),
        collect(
            ['position' => 4, 'point' => 70,],
            ['position' => 4, 'point' => 70,],
        ),
        collect(
            ['position' => 5, 'point' => 60,],
        )
    );

Upvotes: 1

Related Questions