How to sort a dictionary by value in Python

Sorting a dictionary by value is a one-liner. Getting the ties right, and mixing ascending and descending in the same sort, is where it gets interesting.

The answer

ordered = dict(sorted(scores.items(), key=lambda kv: kv[1]))
scores = {'ada': 92, 'alan': 78, 'grace': 92, 'linus': 65}
ascending  : {'linus': 65, 'alan': 78, 'ada': 92, 'grace': 92}
descending : reverse=True

Three things are going on. .items() gives (key, value) pairs, the key= function picks the value out of each pair, and sorted returns a list — so dict() around it turns the result back into a mapping:

type : list
[('linus', 65), ('alan', 78), ('ada', 92), ('grace', 92)]

That last step only preserves the order because dictionaries keep insertion order, which has been guaranteed since Python 3.7. On older versions you would need collections.OrderedDict.

Often you do not want a dict back at all — a list of pairs is easier to work with, and it can hold duplicates.

operator.itemgetter(1) is a slightly faster and arguably clearer alternative to the lambda:

key=itemgetter(1) : [('linus', 65), ('alan', 78), ('ada', 92), ('grace', 92)]

Ties

ada and grace both scored 92. Which comes first?

ascending : ['linus', 'alan', 'ada', 'grace']

ada, because it came first in the original dictionary. Python’s sort is stable: equal elements keep their relative order. That is a guarantee, not an accident, and you can rely on it.

If you want ties broken explicitly, sort by a tuple:

key=lambda kv: (kv[1], kv[0])
by value, then name : ['linus', 'alan', 'ada', 'grace']

Tuples compare element by element, so this reads as “by value, then by name”.

Mixing directions

Here is the one that is genuinely awkward. You want highest score first, and names alphabetically within the same score. reverse=True reverses everything, including the names.

For numbers, negate:

key=lambda kv: (-kv[1], kv[0])
value DESC, name ASC : ['ada', 'grace', 'alan', 'linus']

Scores descending, names ascending within a tie. Negating only works on numbers, of course. For strings the general trick is to sort twice, least significant key first, relying on stability:

rows = sorted(rows, key=itemgetter(0))                # name, ascending
rows = sorted(rows, key=itemgetter(1), reverse=True)  # score, descending

The second sort preserves the first sort’s order among equal scores. This works for any combination of directions and types.

The slip worth naming

keys sorted by value : ['linus', 'alan', 'ada', 'grace']
sorted(scores)       : ['ada', 'alan', 'grace', 'linus']

sorted(scores) iterates the dict, which yields keys, so you get the keys sorted alphabetically. It looks plausible and it is not what you asked for.

If you only want the keys in value order, pass the dict’s own lookup as the key function:

sorted(scores, key=scores.get)

When you only want the top few

Sorting everything to look at three items is wasteful. heapq.nlargest does it without a full sort:

heapq.nlargest(2, scores, key=scores.get) : ['ada', 'grace']

And if you are counting things, Counter has it built in:

Counter('mississippi').most_common(2) : [('i', 4), ('s', 4)]

most_common() is already sorted by count, descending. No key= needed.

Two smaller things

There is no in-place version:

original unchanged : True

Lists have .sort(); dicts do not. sorted() always gives you something new.

And if your key function can fail, it fails loudly, mid-sort:

-> KeyError: 'n'

That is a dict whose values are themselves dicts, one of which is missing the field being sorted on. .get('n', 0) fixes it:

with .get(..., 0) : [('b', {}), ('a', {'n': 1})]

About Netcup (advertisement)

The German host Netcup offers, among other things, affordable and powerful web hosting packages, KVM-based root servers and dedicated servers. With our voucher codes you can save even more (6€ off your first order, 30% off all KVM-based root servers, ...).