Jordi van Selm
Jordi van Selm

Reputation: 75

Iterate dictionary (within dictionary) to sum up ranking

I got the following list that contains 4 tuples with the scores of each player over 7 games:

players_score  = [ ('Joe', 100, 34, 38, 90, 67, 3, 10),
                   ('Bob', 90, 38, 4, 100, 60, 4, 11),
                   ('May', 80, 36, 40, 91, 70, 2, 12),
                   ('Anna', 95, 32, 36, 92, 68, 8, 13) ]

Since I'm trying to learn more about dictionaries, I asked how I could convert the above to a dictionary. I got the following:

# convert player's scores to dictionary
games = {}
for (player_name, *scores) in players_score:
    for game_no, score in enumerate(scores, 1):
        games.setdefault(game_no, {}).setdefault(player_name, {})
        games[game_no][player_name] = score

# the dictionary "games" will look like:

# {1: {'Anna': 95, 'Bob': 90, 'Joe': 100, 'May': 80},
#  2: {'Anna': 32, 'Bob': 38, 'Joe': 34, 'May': 36},
#  3: {'Anna': 36, 'Bob': 4, 'Joe': 38, 'May': 40},
#  4: {'Anna': 92, 'Bob': 100, 'Joe': 90, 'May': 91},
#  5: {'Anna': 68, 'Bob': 60, 'Joe': 67, 'May': 70},
#  6: {'Anna': 8, 'Bob': 4, 'Joe': 3, 'May': 2},
#  7: {'Anna': 13, 'Bob': 11, 'Joe': 10, 'May': 12}}

Now I'm trying out different ways to rank each player's score of each game and then add up those scores to return something like this:

{'Anna': 23, 'May': 18, 'Bob': 16, 'Joe': 15}

The score above is generated when the sum of the ranking of each game is returned. I let the best player, with the highest score of each game earn 4 points, the second one 3, etc.

Of course, it isn't very hard to just sort the outcomes of game 1 like this:

score_list = []
for p in players_score:
    score_list.append(p[1])

score_list.sort(reverse=True)
print(score_list)

Returns: [100, 95, 90, 80]

Would it be any good to search in the list/dictionary for these values a then add the points (4/3/2/1) to the name and finally sum them up?

Upvotes: 1

Views: 141

Answers (1)

Arkistarvh Kltzuonstev
Arkistarvh Kltzuonstev

Reputation: 6935

Try this :

from itertools import zip_longest as zilo
tt = list(sorted(i, reverse=True) for i in zilo(*[v for _, *v in players_score]))
# tt = [[100, 95, 90, 80], [38, 36, 34, 32], [40, 38, 36, 4], [100, 92, 91, 90], [70, 68, 67, 60], [8, 4, 3, 2], [13, 12, 11, 10]]

score_dict = {k : sum(4-j.index(i) for i,j in zip(v, tt)) for k, *v in players_score}

Output :

{'Joe': 15, 'Bob': 17, 'May': 18, 'Anna': 20}

Explanation :

First of all, we get the scores of each players for a game in a tuple with zip_longest from itertools with this :

zipped = list(zilo(*[v for _, *v in players_score]))

Now, zipped is :

[(100, 90, 80, 95), (34, 38, 36, 32), (38, 4, 40, 36), (90, 100, 91, 92), (67, 60, 70, 68), (3, 4, 2, 8), (10, 11, 12, 13)]

Check first item (tuple) is the score of four players for the first game and so on. Now we sort each tuple, in the descending order to this :

tt = list(sorted(i, reverse=True) for i in zipped)

So, tt now becomes :

[[100, 95, 90, 80], [38, 36, 34, 32], [40, 38, 36, 4], [100, 92, 91, 90], [70, 68, 67, 60], [8, 4, 3, 2], [13, 12, 11, 10]]

Now, we need to assign scores for each player. We can break down the one-liner dict-comprehension like this :

score_dict = {}
for k, *v in players_score:
    tmp = []
    for i,j in zip(v, tt):
        tmp.append(4-j.index(i)) #Change your score assigning logic here [5,3,1,0] in stead of [4,3,2,1]
    #print(tmp) #Check here for scores in each game for a player
    tot_score = sum(tmp)
    score_dict[k] = tot_score
#print(score_dict)

We iterate over the players_score, we take the first item of each tuple k in it as player name, the rest as another tuple v (the scores of one player in all games), we then zip this tuple v and previous tt, to assign values for rank in each particular game. Scores assigned are 4-the_sorted_one_game_scores_tuple.index(particular_players_score_in_that_game) and all these scores are being appended to a temporary list for each players. After we get the scores for all the games, we sum them up and assign this summation value tot_score as the value to the key k for the score_dict dictionary.

Update :

You can use 5-2*j.index(i) if 5-2*j.index(i) > 0 else 0 in place of 4-j.index(i). Please note that for that case you must have less than 5 games as more than 5 games would assign the last guy and the second last guy both zero. If you want to generalize this logic to assign 0,1,3,5,7,9,... for more number of persons; then you have use a function 2*x-1 if 2*x-1 > 0 else 0 to generate this number sequence. in that case, change inside the second for loop, where we are appending the scores in a temporary list, like this :

# To get 0,1,3,5,7,9,... type score
score_func = lambda x: 2*x-1 if 2*x-1>0 else 0
score_dict = {}
for k, *v in players_score:
    tmp = []
    for i,j in zip(v, tt):
        tmp.append(score_func(j.index(i)))
    tot_score = sum(tmp)
    score_dict[k] = tot_score
print(score_dict)

Upvotes: 2

Related Questions