0

I'm trying to sort a dict by multiple keys. This is the dict I have:

standings = {1: {1: 1, 2: 0, 3: 1, 4: 0, 5: 0, 'player': 'Jack', 'points': 15},
             2: {1: 1, 2: 0, 3: 2, 4: 2, 5: 0, 'player': 'Kate', 'points': 15},
             3: {1: 0, 2: 0, 3: 1, 4: 0, 5: 0, 'player': 'Sawyer', 'points': 5}}

I want to sort it by, in this order: 'points', 1, 2, 3, 4, 5.

I could do this, I assume:

reversed(sorted(standings, key=lambda x: (standings[x]['points'], 
                                          standings[x][1],
                                          standings[x][2], 
                                          standings[x][3], 
                                          standings[x][4], 
                                          standings[x][5])))

However, the 1, 2, 3, 4, 5 keys are dynamic (and could be 1, 2, 3, 4, 5, 6, 7, 8, 9, etc.)

So, somehow I want to make the sorting keys dynamic in sorted(), except for 'points' which will always be used.

The result I want is a reversed sorted list with the keys (which are player ids from the db) from the first dict. i.e. for the given example it will be[2, 1, 3].

Georgy
  • 12,464
  • 7
  • 65
  • 73
saturnusringar
  • 149
  • 3
  • 13
  • Possible duplicate of [Custom Sorting Python Dictionary](https://stackoverflow.com/questions/12031482/custom-sorting-python-dictionary) – trotta Jul 09 '19 at 07:59

1 Answers1

1

Basically, what you are looking for is itemgetter with range:

from operator import itemgetter

standings = ...  # your dictionary of dictionaries
n = 5  # number of keys to sort on (1, 2, 3, ..., n)
# The following will collect values by 'points', 1, 2, ..., n in a tuple:
get_values = itemgetter('points', *range(1, n + 1))
result = sorted(standings, 
                key=lambda x: get_values(standings[x]), 
                reverse=True)
# [2, 1, 3]

Explanation:

In order to achieve the sorting by several dict keys, you could use itemgetter to create a function that will return a tuple of values by specified keys. So, as a simple example, if you would have this dictionary:

my_dict = {1: 10, 2: 20, 3: 30, 4: 40, 5: 50, 'player': 'Ben'}

and you would want to get the values by keys player, 1 and 2, you would write:

from operator import itemgetter

get_values = itemgetter('player', 1, 2)
get_values(my_dict)
# ('Ben', 10, 20)

Now, as the number of the values can vary and those are actually ordered integers (1, 2, 3, ...), you could unpack the given range to the itemgetter:

get_values = itemgetter('player', *range(1, 4))  # 'player', 1, 2, 3
get_values(my_dict)
# ('Ben', 10, 20, 30)

Finally, for your given example dictionary of dictionaries we get these tuples for each child dictionary and sort by them:

standings = {1: {1: 1, 2: 0, 3: 1, 4: 0, 5: 0, 'player': 'Jack', 'points': 15},
             2: {1: 1, 2: 0, 3: 2, 4: 2, 5: 0, 'player': 'Kate', 'points': 15},
             3: {1: 0, 2: 0, 3: 1, 4: 0, 5: 0, 'player': 'Sawyer', 'points': 5}}
max_key = 5  # you may also calculate it as a max integer key
get_values = itemgetter('points', *range(1, n + 1))
result = sorted(standings, key=lambda x: get_values(standings[x]))
# [3, 1, 2]
# or reversed:
sorted(standings, 
       key=lambda x: get_keys(standings[x]),
       reverse=True)
# [2, 1, 3]
Georgy
  • 12,464
  • 7
  • 65
  • 73