0

Alright so I created a leaderboard and I made it to display the players with the most wins. For some reason when there is multiple people with the same amount of wins it just displays one persons name over and over again.

Here is the main.py

@client.command()
@commands.cooldown(1,5, BucketType.user)
async def leaderboard(ctx, x = 1):
    users = await get_win_data()
    leader_board = {}
    total = []
    for user in users:
        name = int(user)
        total_amount = users[user]["Wins"]
        leader_board[total_amount] = name
        total.append(total_amount)
    
    total = sorted(total, reverse=True)

    demb = discord.Embed(title = f"Top  winners!", descripition = "These are the people with the most amount of wins.", color=discord.Color.red())
    index = 1 
    for wins in total:
        id_ = leader_board[wins]
        member = client.get_user(id_)
        name = member.name
        demb.add_field(name = f"{index}. {name}", value=f"Won **{wins}** times", inline=False)
        if index == x:
            break
        else:
            index += 1 

    
    await ctx.send(embed = demb)

Here is the json

{
    "427924596164132864": {
        "Wins": 1
    },
    "441638109587832842": {
        "Wins": 1
    },
    "479527342860140544": {
        "Wins": 1
    }
}

I was wondering if there was any way to make it display all 3 players even though they're all at the same win count.

Alex is funny
  • 165
  • 1
  • 9
  • Can you explain the use of `x`? Right now your code is mapping score to a single name (this is why it doesn't have 3 different names), adding each score to a list, sorting it in descending order and looping over that. It would be much better to sort and loop over the `users` dict that you already have. – shriakhilc Dec 26 '21 at 04:49
  • Is that to limit the leaderboard to only `x` positions? – shriakhilc Dec 26 '21 at 04:50
  • @shriakhilc Correct when someone runs ?leaderboard 5 it will display 5 positions if it so the 5 in that command would be the x – Alex is funny Dec 26 '21 at 05:01

1 Answers1

0

A dictionary can directly access (key, value) pairs using items() and then sort based on value as described at How do I sort a dictionary by value?

The final simplified code will be:

@client.command()
@commands.cooldown(1,5, BucketType.user)
async def leaderboard(ctx, x = 1):
    users = await get_win_data()
    iterator = sorted(users.items(), key=lambda item: item[1]["Wins"], reverse=True)
    demb = discord.Embed(title = f"Top  winners!", descripition = "These are the people with the most amount of wins.", color=discord.Color.red())
    
    count = 1
    for id_, data in iterator:
        member = client.get_user(id_)
        if member is not None:
            demb.add_field(name = f"{count}. {member.name}", value=f"Won **{data['Wins']}** times", inline=False)
            if count == x:
                break
            else:
                count += 1
        else:
            # Handle missing users based on your application
            print(f"user ID {id_} is missing.")
    
    await ctx.send(embed = demb)

Just like in your code, this takes the first x values in descending order. If you want, you can modify the lambda passed to sorted() to enforce additional ordering for when wins are the same. By default they'll just maintain whatever order is already in users.


Your original code could have been made to work (for example, by mapping each score to a list rather than an int name directly), but those intermediate structures themselves are not necessary. The main cause of your bug was that each score only maps to one name, so the last name with that score is the only one it knows. Meanwhile total had that score stored multiple times, and each time it would print the same (last) name.


Update based on chat: Apart from some typos, the main issue was that the get_user discord API returns None if it can't find the user. OP will be looking into that problem, but this code solves the sorting issue if we just add a simple if member is not None check.

shriakhilc
  • 2,922
  • 2
  • 12
  • 17
  • The code you're providing still displays one name when the command is ran – Alex is funny Dec 26 '21 at 05:53
  • By one name, do you mean it displays a single person (because x=1 by default) or does it repeat the same `name` multiple times? If your JSON for `users` is correct, this is not possible. If the json itself can have duplicates, then additional conditions will be needed. Also can you check if it is just the `name` repeating or even the `id_`? You can just add `id_` to the f-string too. – shriakhilc Dec 26 '21 at 12:55
  • @shriakhlic it repeats the same name multiple times – Alex is funny Dec 26 '21 at 16:25
  • Unless multiple `id_` have the same username on your discord server, or information is incorrect in the question, I can't think of why this would be failing. If you'd like to debug further, we can discuss in a chat room. – shriakhilc Dec 26 '21 at 16:44
  • Yeah this is quite bizarre, we could discuss in a chat room only if you would like though, I would not want to waste your time. – Alex is funny Dec 26 '21 at 18:18
  • Yeah, here's the [link to the chat room](https://chat.stackoverflow.com/rooms/240457/70484244-multiple-values-in-discord-leaderboard). – shriakhilc Dec 26 '21 at 19:58
  • 1
    alright I have joined – Alex is funny Dec 27 '21 at 20:05