How can I order entries in my OrderedDict where values are duplicated?

Question:

I have a bunch of code that is out of scope of this question. The code scrapes data and gives me the following dictionary:

{‘Male Open 60 Mins’: {‘Kavin Jones’: {‘total_points’: 47,
‘plate_number’: ’35’}, ‘Alan Smith’: {‘total_points’: 42,
‘plate_number’: ’23’}, ‘Dominique Smith’: {‘total_points’: 35,
‘plate_number’: ’20’}, ‘James Smith’: {‘total_points’: 32,
‘plate_number’: ’15’}, ‘Hans Smith’: {‘total_points’: 35,
‘plate_number’: ’99’}, ‘Cameron Smith’: {‘total_points’: 33,
‘plate_number’: ’21’}, ‘Michael Smith’: {‘total_points’: 23,
‘plate_number’: ‘148’}, ‘Chris Smith’: {‘total_points’: 32,
‘plate_number’: ’18’}, ‘Jackson Smith’: {‘total_points’: 14,
‘plate_number’: ’17’}, ‘Grant Smith’: {‘total_points’: 12,
‘plate_number’: ’48’}, ‘David Smith’: {‘total_points’: 11,
‘plate_number’: ‘3’}, ‘Andrew Smith’: {‘total_points’: 23,
‘plate_number’: ’32’}, ‘Duncan Smith’: {‘total_points’: 22,
‘plate_number’: ’33’}, ‘Brian Smith’: {‘total_points’: 18,
‘plate_number’: ’36’}, ‘Manning Jones’: {‘total_points’: 10,
‘plate_number’: ’14’}, ‘Matthew Smith’: {‘total_points’: 15,
‘plate_number’: ’27’}, ‘Clifford Smith’: {‘total_points’: 6,
‘plate_number’: ’28’}, "Che’quan Smith": {‘total_points’: 4,
‘plate_number’: ’34’}, ‘Philip Smith’: {‘total_points’: 9,
‘plate_number’: ’43’}, ‘Christopher Jones’: {‘total_points’: 4,
‘plate_number’: ’13’}, ‘Wendell Smith’: {‘total_points’: 5,
‘plate_number’: ‘2’}, ‘Maceo Smith’: {‘total_points’: 2,
‘plate_number’: ‘8’}, ‘Craig Smith’: {‘total_points’: 2,
‘plate_number’: ’11’}, ‘Dennis Smith’: {‘total_points’: 2,
‘plate_number’: ’10’}, ‘Edwin Smith’: {‘total_points’: 2,
‘plate_number’: ’88’}, ‘Dirk Smith’: {‘total_points’: 2,
‘plate_number’: ‘142’}, ‘Mark Smith’: {‘total_points’: 12,
‘plate_number’: ’46’}, ‘Jay Smith’: {‘total_points’: 2,
‘plate_number’: ’25’}, ‘Blake Smith’: {‘total_points’: 2,
‘plate_number’: ‘178’}, ‘Stephen Smith’: {‘total_points’: 1,
‘plate_number’: ’31’}, ‘Justin Smith’: {‘total_points’: 2,
‘plate_number’: ’12’}, ‘Peter Smith’: {‘total_points’: 2,
‘plate_number’: ’26’}, ‘Jenai Smith’: {‘total_points’: 1,
‘plate_number’: ’29’}, ‘Christopher Smith’: {‘total_points’: 2,
‘plate_number’: ’44’}, ‘Nathan j. Smith’: {‘total_points’: 2,
‘plate_number’: ’39’}, ‘Deryck Smith’: {‘total_points’: 1,
‘plate_number’: ’24’}, ‘Conor Smith’: {‘total_points’: 25,
‘plate_number’: ’40’}, ‘Moses Smith’: {‘total_points’: 7,
‘plate_number’: ’16’}, ‘Howard Smith’: {‘total_points’: 5,
‘plate_number’: ’41’}, ‘Chad Smith’: {‘total_points’: 1,
‘plate_number’: ’38’}}, ‘Female Open 60 Mins’: {‘Jennifer Smith’:
{‘total_points’: 67, ‘plate_number’: ’42’}, ‘Ashley Smith’:
{‘total_points’: 40, ‘plate_number’: ‘6’}, ‘Caitlin Smith’:
{‘total_points’: 25, ‘plate_number’: ‘5’}}}

I then run the following code to order each racer within each race by total_points:

from collections import OrderedDict
from operator import getitem

for key in overall_standings.keys():
    current_reorder = OrderedDict(sorted(overall_standings[key].items(), key = lambda x: getitem(x[1], 'total_points'), reverse=True))
    overall_standings[key] = current_reorder

    for item, racer_info in overall_standings.items():
        print("n********" + item + "********")
        for name, data in racer_info.items():
            print(name)
            print(data['total_points'])
            print(data['plate_number'])

If I print out the ordered dictionary now, it looks like this:

{‘Male Open 60 Mins’: OrderedDict([(‘Kavin Jones’, {‘total_points’: 47, ‘plate_number’: ’35’}), (‘Alan Smith’, {‘total_points’: 42, ‘plate_number’: ’23’}), (‘Dominique Smith’, {‘total_points’: 35, ‘plate_number’: ’20’}), (‘Hans Smith’, {‘total_points’: 35, ‘plate_number’: ’99’}), (‘Cameron Smith’, {‘total_points’: 33, ‘plate_number’: ’21’}), (‘James Smith’, {‘total_points’: 32, ‘plate_number’: ’15’}), (‘Chris Smith’, {‘total_points’: 32, ‘plate_number’: ’18’}), (‘Conor Smith’, {‘total_points’: 25, ‘plate_number’: ’40’}), (‘Michael Smith’, {‘total_points’: 23, ‘plate_number’: ‘148’}), (‘Andrew Smith’, {‘total_points’: 23, ‘plate_number’: ’32’}), (‘Duncan Smith’, {‘total_points’: 22, ‘plate_number’: ’33’}), (‘Brian Smith’, {‘total_points’: 18, ‘plate_number’: ’36’}), (‘Matthew Smith’, {‘total_points’: 15, ‘plate_number’: ’27’}), (‘Jackson Smith’, {‘total_points’: 14, ‘plate_number’: ’17’}), (‘Grant Smith’, {‘total_points’: 12, ‘plate_number’: ’48’}), (‘Mark Smith’, {‘total_points’: 12, ‘plate_number’: ’46’}), (‘David Smith’, {‘total_points’: 11, ‘plate_number’: ‘3’}), (‘Manning Jones’, {‘total_points’: 10, ‘plate_number’: ’14’}), (‘Philip Smith’, {‘total_points’: 9, ‘plate_number’: ’43’}), (‘Moses Smith’, {‘total_points’: 7, ‘plate_number’: ’16’}), (‘Clifford Smith’, {‘total_points’: 6, ‘plate_number’: ’28’}), (‘Wendell Smith’, {‘total_points’: 5, ‘plate_number’: ‘2’}), (‘Howard Smith’, {‘total_points’: 5, ‘plate_number’: ’41’}), ("Che’quan Smith", {‘total_points’: 4, ‘plate_number’: ’34’}), (‘Christopher Jones’, {‘total_points’: 4, ‘plate_number’: ’13’}), (‘Maceo Smith’, {‘total_points’: 2, ‘plate_number’: ‘8’}), (‘Craig Smith’, {‘total_points’: 2, ‘plate_number’: ’11’}), (‘Dennis Smith’, {‘total_points’: 2, ‘plate_number’: ’10’}), (‘Edwin Smith’, {‘total_points’: 2, ‘plate_number’: ’88’}), (‘Dirk Smith’, {‘total_points’: 2, ‘plate_number’: ‘142’}), (‘Jay Smith’, {‘total_points’: 2, ‘plate_number’: ’25’}), (‘Blake Smith’, {‘total_points’: 2, ‘plate_number’: ‘178’}), (‘Justin Smith’, {‘total_points’: 2, ‘plate_number’: ’12’}), (‘Peter Smith’, {‘total_points’: 2, ‘plate_number’: ’26’}), (‘Christopher Smith’, {‘total_points’: 2, ‘plate_number’: ’44’}), (‘Nathan j. Smith’, {‘total_points’: 2, ‘plate_number’: ’39’}), (‘Stephen Smith’, {‘total_points’: 1, ‘plate_number’: ’31’}), (‘Jenai Smith’, {‘total_points’: 1, ‘plate_number’: ’29’}), (‘Deryck Smith’, {‘total_points’: 1, ‘plate_number’: ’24’}), (‘Chad Smith’, {‘total_points’: 1, ‘plate_number’: ’38’})]), ‘Female Open 60 Mins’: OrderedDict([(‘Jennifer Smith’, {‘total_points’: 67, ‘plate_number’: ’42’}), (‘Ashley Smith’, {‘total_points’: 40, ‘plate_number’: ‘6’}), (‘Caitlin Smith’, {‘total_points’: 25, ‘plate_number’: ‘5’}}}

I’m not sure how OrderedDict or sorted sorts the racers where they have the same number of points. You’ll see that there are a bunch of racers where the total_points are 2 and 1.

What I’m looking to do, is to add to the dictionary a key for each racer where it averages their race positions. Take Chad Smith for instance. Let’s say that he came 10th, 11th, 10th, and 9th. I want to add a key where his race position average is 10. This is easy to do.

How do I take the OrderedDict now and say, for every duplicate in total_points, I want to now sort by race position average? Would this code come during my primary OrderedDict and sorted code or would it come after?

Asked By: Eddi

||

Answers:

I learned that built into sorted is a tuple value where you can sort by a secondary field if there is a tie in your first field.

I added the following lines to my code to allow for the list addition. If this racer is new, we create a var with the empty list.

if racer not in overall_standings[race_name]:
    overall_standings[race_name][racer] = {}    
    if "average_position" not in overall_standings[race_name][racer]:
        overall_standings[race_name][racer]["average_position"] = []
    overall_standings[race_name][racer]["average_position"].append(position)
else:
    overall_standings[race_name][racer]["average_position"].append(position)

Could you do this cleaner? Sure. But this works. Code clean-up comes later.

This would give me a dictionary that looks like:

{‘Male Open 60 Mins’: {‘Kavin Smith’: {‘average_position’: [1, 2],
‘total_points’: 47, ‘plate_number’: ’35’}, ‘Alan Potts’:
{‘average_position’: [2, 3], ‘total_points’: 42, ‘plate_number’: ’23’}}}

I then averaged out the list:

for item, racer_info in overall_standings.items():
for name, data in racer_info.items():
    data["average_position"] = sum(data["average_position"])/len(data["average_position"])

Finally I do the following from the code in my original question:

for key in overall_standings.keys():
    current_reorder = OrderedDict(sorted(overall_standings[key].items(), key = lambda x: (getitem(x[1], 'total_points'), -getitem(x[1], 'average_position')), reverse=True))
    overall_standings[key] = current_reorder

I do -getitem(x[1] with a minus because you can only set the reverse= switch once and while I want the total_points to order from biggest to smallest, I want the average_position to order from smallest to biggest (because if you and another racer both have 20 points, coming 1st beats coming 2nd overall).

Now I just access the data.

for item, racer_info in overall_standings.items():
    print("n------------" + item + "------------")
    for name, data in racer_info.items():
        print(name)
        print("Total Points: " + str(data['total_points']))
        print("Plate Number: " + str(data['plate_number']))
        print("Average Position: " + str(data['average_position']))
        print("-----------------------")
Answered By: Eddi