Your main problem is that you've mapped scores to names:
scores[score]=name
What if two people have the same score? score[score] = name
would overwrite (lose) one of the names since a dict
can only may one key (e.g. score) to one value (e.g. name). Therefore, instead, you need to map names to a list of scores:
scores.setdefault(name, []).append(name)
The setdefault
method returns scores[name]
if name
is in scores
, and returns a new empty list []
, assigned to scores[name]
, otherwise.
Sorting:
With scores
being a dict mapping names to scores, sorting the names alphebatically is easy: you could use sorted(scores)
.
To sort scores
by maximum score from highest to lowest, you could use
sorted(scores, key=lambda name: max(scores[name]), reverse=True)
See HOWTO Sort for an excellent tutorial on sorting, including the use of the key
parameter.
Keeping the last three values:
To store just the last three values for each name, you could use a collections.deque
, which is a list-like container which can have a maximum length. As items are appended to the deque, older items are dropped if the maximum length has been reached.
For example, here is a deque with maximum length 3:
In [100]: d = collections.deque(maxlen=3)
We can insert three values:
In [101]: d.extend([1,2,3])
In [102]: d
Out[102]: deque([1, 2, 3], maxlen=3)
But when we insert a fourth value, only the last three are kept:
In [103]: d.append(4)
In [104]: d
Out[104]: deque([2, 3, 4], maxlen=3)
Thus, to sort the names according to the maximum of the last 3 scores per person,
you could use:
import collections
scores = {}
with open("results.txt") as resultfile:
for line in resultfile:
name, score = line.split()
scores.setdefault(name, collections.deque(maxlen=3)).append(float(score))
print("The top and average scores were:")
for name in sorted(scores, key=lambda name: max(scores[name]), reverse=True):
ave = sum(scores[name])/len(scores[name])
m = max(scores[name])
print('{name}: {m} {a}'.format(name=name, m=m, a=ave))
An alternative to avoid the double computation:
One weakness of the above code is that the quantity max(scores[name])
is computed twice: once in the call to sorted
, and once inside the for-loop
.
One way to avoid this double computation is to precompute the values once and store the results in a list, data
:
data = []
for name, vals in scores.items():
m = max(vals)
ave = sum(vals)/len(vals)
data.append((ave, name, m))
data
is now a list of tuples. Each tuple has the form (ave, name, m)
.
Sorting a list of tuples is done lexicographically. The tuples are sorted according to the first element, with the second element used to break ties, and then the third to break any remaining ties, and so on.
So
for ave, name, m in sorted(data, reverse=True):
print('{name}: {m} {a}'.format(name=name, m=m, a=ave))
would iterate over the tuples in data
, from highest average to lowest average, and the averages are only computed once. The disadvantage of doing it this way is that it requires more memory (to store data
). So the two options shown above each has a pro and con. The first method requires less memory, the second requires less computation.