Ranking players in a league by sorting the contents of an OrderedDict

I'm new to python. I'm having second thoughts on my approach to this problem:

The LeagueTable class tracks the score of each player in a league. After each game, the player records their score with the record_result function. The player's rank in the league is calculated using the following

logic:

1. The player with the highest score is ranked first (rank 1). The player with the lowest score is ranked last.
2. If two players are tied on score, then the player who has played the fewest games is ranked higher.
3. If two players are tied on score and number of games played, then the player who was first in the list of players is ranked higher.

Implement the player_rank function that returns the player at the given rank.

example:

table = LeagueTable(['Mike', 'Chris', 'Arnold'])
table.record_result('Mike', 2)
table.record_result('Mike', 3)
table.record_result('Arnold', 5)
table.record_result('Chris', 5)
print(table.player_rank(1))


All players have the same score. However, Arnold and Chris have played fewer games than Mike, and as Chris is before Arnold in the list of players, he is ranked first. Therefore, the code above should display "Chris".

I have a solution, but I want to know if there is a more optimised approach to this.

from collections import Counter
from collections import OrderedDict

class LeagueTable:
def __init__(self, players):
self.standings = OrderedDict([(player, Counter()) for player in players])
def record_result(self, player, score):
self.standings[player]['games_played'] += 1
self.standings[player]['score'] += int(score)# print(self.standings)# print("")
def player_rank(self, rank): #res = OrderedDict()
x=self.standings
d=self.standings
def swap_list(xl,co):
xl[co],xl[co+3]=xl[co+3],xl[co]
xl[co+1],xl[co+4]=xl[co+4],xl[co+1]
xl[co+2],xl[co+5]=xl[co+5],xl[co+2]
return(xl,co)

player_list=[]
lc=len(x)
lc1=lc
lc=lc*3
for p,c in x.items():
player_list.append(p)
player_list.append(c["score"])
player_list.append(c["games_played"])
for j in range(lc1):
for i in range(0,lc-3,3):
# print(i+4)
if (player_list[i+1] < player_list[i+4]):
swap_list(player_list,i)
elif (player_list[i+1]==player_list[i+4]):
if(player_list[i+2]>player_list[i+5]):
swap_list(player_list,i)
elif(player_list[i+2]==player_list[i+5]):
pass

return(player_list[(rank-1)*3])

table = LeagueTable(['Mike', 'Chris', 'Arnold'])
table.record_result('Mike', 2)
table.record_result('Mike', 3)
table.record_result('Arnold', 5)
table.record_result('Chris', 5)
rk=int(input("enter rank \n"))
print(table.player_rank(rk))


First of all, your entire def player_rank: is way over-engineered.. and with the correct standard modules this practically becomes a one-liner

Review

• Use better variable names (naming is hard), but to me names like c, x makes no sense. I don't know what they do at all.
• Use built in methods, like sort() this greatly reduces complexity.
• With the full powers of sorted() you can sort multiple items at once.
• With a - before the item we are sorting you can reverse the order, that makes it easier to sort multiple items on different orders.

With my changes I get the following code.

from collections import OrderedDict, Counter

class LeagueTable:
def __init__(self, players):
self.standings = OrderedDict([(player, Counter()) for player in players])

def record_result(self, player, score):
self.standings[player]['games_played'] += 1
self.standings[player]['score'] += score

def player_rank(self, rank):
return sorted(self.standings, key=lambda p: (-self.standings[p]['score'],
self.standings[p]['games_played']))[rank-1]

• I think it would be good to add a cache on the sorted rank (which is cleared if another player is added). While this is a great answer for the current test case, it will involve sorting on every call to player_rank even if the order hasn't changed. This will result in unnecessary computation if the end user is just trying to get out the full player order.l through repeated calls to player_rank. Of course, if this weren't a test problem, a real life solution would have additional public methods to query the rank in different ways. – Conor Mancone Nov 7 '17 at 11:54
• That would sort by each player's average score per game. The challenge calls for sorting by the total score, with the first tie-breaking criterion being the number of games played, and the second tie-breaking criterion being the position in the roster. – 200_success Nov 7 '17 at 18:49
• Note that sorted() is guaranteed to be stable, according to the docs. Your pos field is unnecessary - the OrderedDict should iterate the items in order, and the sort should maintain the relative order of otherwise identical items. – Austin Hastings Nov 7 '17 at 21:25
• @Ludisposed this isn't the result expected. I wish if it was this simple, something like multiple argument based sorting. If there is no tie in score, then sorting should only be based on score, However if there is a tie, then the next factor is lower number of games played, and only if its also a tie, then ranking should be performed on who entered first. The expected result is rank 1: Chris, Rank2: Arnold, Rank 3: Mike – Vipin Mohan R Nair Nov 8 '17 at 3:54
• @Vipin Mohan R Nair This is valid, try it put yourself on the site you gave. I have 4/4 passed. – Ludisposed Nov 8 '17 at 6:33