PDavis PDavis - 3 months ago 5
Python Question

Sort list of dictionaries on two criteria

Recently started programming with python and I have a question to which I cannot come up with an answer. I have a large list of dictionairies with various keys and values (more than below). I want to sort the order of the dictionaries appearing in the list.

list_one
:

list_one = [{'country': 'Spain', 'id': 'v1', 'key2': 'value2'},
{'country': 'France', 'id': 'v4', 'key2': 'value2'},
{'country': 'China', 'id': 'v4', 'key2': 'value2'},
{'country': 'Russia', 'id': 'v3', 'key2': 'value2'},
{'country': 'Australia', 'id': 'v2', 'key2': 'value2'},
{'country': 'China', 'id': 'v3', 'key2': 'value2'},
...
]


First, sort on 'id' value (
v1
,
v2
,
v3
, ...) (which I can work out fine).

Additionally, if
id
values are similar, sort according to the value of the
'country'
key. Bu I do not want to sort these alphabetically. I'd like to be able to sort based on values set to these countries, if that makes sense. So, for example,
France = 1
,
China = 2
,
Australia = 3
,
Spain = 4
,
Russia = 5
.

The ordering of dictionaries should then look as in the second
example.
list_two
:

[{'country': 'Spain', 'id': 'v1', 'key2': 'value2'},
{'country': 'Australia', 'id': 'v2', 'key2': 'value2'},
{'country': 'China', 'id': 'v3', 'key2': 'value2'},
{'country': 'Russia', 'id': 'v3', 'key2': 'value2'},
{'country': 'France', 'id': 'v4', 'key2': 'value2'},
{'country': 'China', 'id': 'v4', 'key2': 'value2'}
...
]


Is there a pythonic way to sort the order that those dicts appear in the list, first on the value of the 'id' key, like below, and then on some 'implicit values' set to the countries?

list_two = sorted(list_one, key=lambda k: k['id'])

Answer

Produce a dictionary mapping country name to order number:

country_ordering = {'France': 1, 'China': 2, 'Australia': 3,
                    'Spain': 4, 'Russia': 5}

then use that in your sort key, returning a tuple with the id and a value from that mapping:

list_two = sorted(
    list_one, 
    key=lambda k: (k['id'], country_ordering.get(k['country'], float('inf')))

I used the dict.get() method to look up the ordering value; that way you can specify a default value in case the country is not (yet?) listed in the mapping. Above I used float('inf') (infinity) as the default value, meaning that any such unlisted countries are sorted at the end (per given id).

If you want an exception to be thrown instead, change the lambda to:

lambda k: (k['id'], country_ordering[k['country']])

to do a straight key lookup.

Demo:

>>> list_one = [{'country': 'Spain',     'id': 'v1', 'key2': 'value2'},
...             {'country': 'France',    'id': 'v4', 'key2': 'value2'},
...             {'country': 'China',     'id': 'v4', 'key2': 'value2'},
...             {'country': 'Russia',    'id': 'v3', 'key2': 'value2'},
...             {'country': 'Australia', 'id': 'v2', 'key2': 'value2'},
...             {'country': 'China',     'id': 'v3', 'key2': 'value2'}]
>>> country_ordering = {'France': 1, 'China': 2, 'Australia': 3, 'Spain': 4, 'Russia': 5}
>>> sorted(list_one, key=lambda k: (k['id'], country_ordering[k['country']]))
[{'country': 'Spain', 'id': 'v1', 'key2': 'value2'}, {'country': 'Australia', 'id': 'v2', 'key2': 'value2'}, {'country': 'China', 'id': 'v3', 'key2': 'value2'}, {'country': 'Russia', 'id': 'v3', 'key2': 'value2'}, {'country': 'France', 'id': 'v4', 'key2': 'value2'}, {'country': 'China', 'id': 'v4', 'key2': 'value2'}]
>>> pprint(_)
[{'country': 'Spain', 'id': 'v1', 'key2': 'value2'},
 {'country': 'Australia', 'id': 'v2', 'key2': 'value2'},
 {'country': 'China', 'id': 'v3', 'key2': 'value2'},
 {'country': 'Russia', 'id': 'v3', 'key2': 'value2'},
 {'country': 'France', 'id': 'v4', 'key2': 'value2'},
 {'country': 'China', 'id': 'v4', 'key2': 'value2'}]
Comments