Problem Solved

Python · sort dictionary

Home › Courses › Python › Problems › Sort a dictionary
🐍 Concept · 3 min read

How to sort a dict by value.
sorted() + a key function.

The most-asked Python dictionary question. The answer is always sorted(d.items(), key=...) — but the details matter. sorted() returns a list, not a dict. To sort by value, use item[1]. To sort by key, use item[0]. To go descending, use reverse=True. And since Python 3.7, you can convert back to a dict and keep the sorted order. This page covers each case, plus the itemgetter alternative.

Short answer: sorted(d.items(), key=lambda item: item[1]) — sorts by value, returns a list of (key, value) tuples. For descending: add reverse=True. To get a dict back: dict(sorted(d.items(), key=lambda item: item[1])) (Python 3.7+ preserves order). Sort by key: item[0]. For speed and readability, operator.itemgetter(1) replaces the lambda. Dicts don't have a .sort() method — always use sorted().

✅ The basic pattern

By value. By key. Ascending. Descending.

sort.py
scores = {'Alice': 85, 'Bob': 72, 'Charlie': 95}

# Sort by value — ascending
by_value = sorted(scores.items(), key=lambda item: item[1])
print(by_value)
# [('Bob', 72), ('Alice', 85), ('Charlie', 95)]

# Sort by value — descending
by_value_desc = sorted(scores.items(), key=lambda item: item[1], reverse=True)
print(by_value_desc)
# [('Charlie', 95), ('Alice', 85), ('Bob', 72)]

# Sort by key
by_key = sorted(scores.items(), key=lambda item: item[0])
print(by_key)
# [('Alice', 85), ('Bob', 72), ('Charlie', 95)]

# Convert back to dict — preserves sorted order
by_value_dict = dict(sorted(scores.items(), key=lambda item: item[1]))
print(by_value_dict)
# {'Bob': 72, 'Alice': 85, 'Charlie': 95}

# Using itemgetter — faster, more readable
from operator import itemgetter

by_value = sorted(scores.items(), key=itemgetter(1))
print(by_value)
# [('Bob', 72), ('Alice', 85), ('Charlie', 95)]

by_key = sorted(scores.items(), key=itemgetter(0))
print(by_key)
# [('Alice', 85), ('Bob', 72), ('Charlie', 95)]

# Sort by multiple indices: value, then key
from operator import itemgetter
ties = sorted(scores.items(), key=itemgetter(1, 0))
Why item[1] for value:

d.items() yields tuples:
  ('Alice', 85)
  ('Bob', 72)
  ('Charlie', 95)

Index 0 is the key.
Index 1 is the value.

sorted(..., key=lambda item: item[1])
  → sorts by index 1 of each tuple
  → value-based sort

sorted(..., key=lambda item: item[0])
  → sorts by index 0
  → key-based sort

sorted(d.items())
  → default: compares tuples element by element
  → by key first, then by value for equal keys
  → equivalent to sort by key, then value

reverse=True
  → sorts descending
  → applies to whatever the key returns
  → works for strings, numbers, tuples

For dicts with mixed types:
  sorted(d.items(), key=lambda x: str(x[0]))
  → converts key to string for comparison
  → rarely needed; usually dict keys are uniform

For case-insensitive key sort:
  sorted(d.items(), key=lambda x: x[0].lower())
  → sort by lowercase key
  → useful for dictionaries with user names

For descending by value, ascending by key:
  Can't do this with a single reverse.
  Use a custom key:
  sorted(d.items(), key=lambda x: (-x[1], x[0]))
  → negative value for descending
  → key for ascending tiebreaker

  Only works for numeric values (can't negate strings).

For general mixed sorting:
  use two passes with sort stability:
  items = sorted(d.items(), key=lambda x: x[0])       # key asc
  items = sorted(items, key=lambda x: x[1], reverse=True)  # value desc
  Stable sort preserves the first order for ties.

✅ Why you get a list

Dicts aren't sequences.

why-list.py
d = {'a': 1, 'b': 2, 'c': 3}

# sorted() on a dict — iterates keys
print(sorted(d))  # ['a', 'b', 'c']
# That's sorted keys, not a sorted dict.

# sorted() on .items() — returns list of tuples
print(sorted(d.items()))
# [('a', 1), ('b', 2), ('c', 3)]

# sorted() always returns a list, regardless of input.
print(type(sorted(d)))            # 
print(type(sorted(d.items())))    # 

# To get a dict back, wrap it:
sorted_dict = dict(sorted(d.items(), key=lambda x: x[1], reverse=True))
print(sorted_dict)  # {'c': 3, 'b': 2, 'a': 1}
print(type(sorted_dict))  # 

# Since Python 3.7, the dict preserves insertion order.
# So the sorted order is the order of the new dict.

# Before Python 3.7 (rare today), you'd use OrderedDict:
from collections import OrderedDict
old_style = OrderedDict(sorted(d.items(), key=lambda x: x[1]))

# For modern Python, dict(sorted(...)) is enough.

# Sorting doesn't modify the original:
d_original = {'a': 3, 'b': 1, 'c': 2}
d_sorted = dict(sorted(d_original.items(), key=lambda x: x[1]))

print(d_original)  # {'a': 3, 'b': 1, 'c': 2}  — unchanged
print(d_sorted)    # {'b': 1, 'c': 2, 'a': 3}  — new dict
The reason sorted() returns a list:

sorted() works on any iterable.
For a dict, the natural iteration is over keys.
For d.items(), it's over tuples.
sorted() returns whatever type it produced —
and that's always a list.

The chain:

  dict → iterable (via .items()) → sorted list
       → optionally back to dict

  Or:

  dict → list of (k, v) tuples → sorted list
       → optionally dict(...) to convert

The conversion step matters:

✅ sorted(d.items(), key=...) — a list of tuples
   You can:
   - Iterate it
   - Slice it (top 5: sorted(...)[:5])
   - Convert to dict: dict(sorted(...))
   - Filter it with a comprehension
   - Pass it to another function

✅ dict(sorted(...)) — a dict
   You can:
   - Look up by key (O(1))
   - Iterate in insertion order (which is sorted)
   - Convert back to a list

The list is more useful for intermediate steps.
The dict is more useful for lookups.

Real-world sorting patterns:

1. Top-N by value:
   top_5 = sorted(d.items(), key=lambda x: x[1], reverse=True)[:5]
   → Keep it as a list. No need to convert back.

2. Full sorted dict:
   d = dict(sorted(d.items(), key=lambda x: x[1]))
   → The dict preserves the order.

3. Convert to string for display:
   for name, score in sorted(d.items(), key=lambda x: -x[1]):
       print(f'{name}: {score}')
   → Iterate the sorted list directly.

4. Sort and keep the rest:
   first, *rest = sorted(d.items(), key=lambda x: x[1])
   → Tuple unpacking on the sorted list.

The common mistake:

❌ d.sort(key=...)  # AttributeError
   Dicts don't have .sort().

❌ sorted(d, key=lambda x: x[1])  # IndexError
   sorted(d) yields keys (strings), not tuples.
   x[1] on a string is indexing.

✅ sorted(d.items(), key=lambda x: x[1])
   Sort the (key, value) tuples.

✅ sorted(d.items())
   Default sort by key, then value.

✅ Common patterns

The cases that come up in real code.

patterns.py
scores = {'Alice': 85, 'Bob': 72, 'Charlie': 95, 'Dave': 85}

# 1. Top N by value
top_2 = sorted(scores.items(), key=lambda x: x[1], reverse=True)[:2]
print(top_2)
# [('Charlie', 95), ('Alice', 85)]

# 2. Sort and keep as list (no dict conversion)
for name, score in sorted(scores.items(), key=lambda x: -x[1]):
    print(f'{name}: {score}')

# 3. Sort by value, then by key for ties
sorted_by_value_then_key = sorted(
    scores.items(),
    key=lambda x: (-x[1], x[0])  # -value (descending), key (ascending)
)
print(sorted_by_value_then_key)
# [('Charlie', 95), ('Alice', 85), ('Dave', 85), ('Bob', 72)]

# 4. Sort by length of key
by_key_length = sorted(scores.items(), key=lambda x: len(x[0]))
print([k for k, _ in by_key_length])
# ['Bob', 'Dave', 'Alice', 'Charlie']

# 5. Sort by custom function
def score_category(score):
    return 'high' if score >= 90 else 'medium' if score >= 80 else 'low'

by_category = sorted(scores.items(), key=lambda x: score_category(x[1]))
print(by_category)

# 6. Filter then sort
passing = sorted(
    ((k, v) for k, v in scores.items() if v >= 80),
    key=lambda x: x[1],
    reverse=True
)
print(passing)
# [('Charlie', 95), ('Alice', 85), ('Dave', 85)]

# 7. Sort keys only, then look up
sorted_keys = sorted(scores)
for key in sorted_keys:
    print(key, scores[key])
Pattern 1: Top N
  sorted(items, key=...)[:N]
  → slice the sorted list
  → no need to convert to dict
  → O(n log n) for the sort, then O(N) for the slice
  → for very large N, use heapq.nlargest (O(n log N))

Pattern 2: Iterate in order
  for k, v in sorted(items, key=...):
      ...
  → most common
  → the list is temporary
  → no dict conversion needed

Pattern 3: Two-level sort
  key=lambda x: (-x[1], x[0])
  → descending by value, ascending by key
  → works for numeric values (negation)
  → for strings or mixed, use two-pass stable sort

Pattern 4: Sort by custom property
  key=lambda x: len(x[0])  # key length
  key=lambda x: x[1].score  # if value is an object
  → any function works as a key
  → return anything sortable

Pattern 5: Category sort
  key=lambda x: category(x[1])
  → group by category
  → stable sort preserves order within category
  → sort by category, then by value for ties:
    key=lambda x: (category(x[1]), x[1])

Pattern 6: Filter then sort
  sorted((x for x in items if cond(x)), key=...)
  → filter first (fewer items to sort)
  → sort the filtered list
  → or sort then filter, but filter-first is faster
  → the generator expression avoids a temporary list

Pattern 7: Keys only
  sorted(d)  # sorted keys
  → works because iterating a dict yields keys
  → useful when you only need order, not values

The stable sort guarantee:

  Python's sort is stable.
  Equal elements preserve their relative order.
  This is why two-pass sorting works:
    sorted(d, key=lambda x: x[1])       # by value
    sorted(d, key=lambda x: x[0])       # then by key
  → first pass by value, second pass by key
  → result: sorted by key, ties broken by value
  → equivalently: sorted(d, key=lambda x: (x[0], x[1]))

The tuple key is the idiomatic way to do
multi-level sorting. The two-pass is equivalent
but more verbose. Use the tuple key.

For simple sorts, use a lambda.
For complex sorts, use a named function.
For performance-critical sorts, use itemgetter.

The pattern you'll write most often:

  dict(sorted(d.items(), key=lambda x: x[1], reverse=True))
  
  → sort by value descending
  → convert back to dict
  → the dict preserves the sorted order
  → iterate as many times as you want

Don't call .sort() on a dictionary. Dicts don't have a .sort() method — calling it raises AttributeError: 'dict' object has no attribute 'sort'. The only way to sort a dict is sorted(), which returns a list. To 'sort a dict in place', reassign: d = dict(sorted(d.items(), key=...)). The original dict is unchanged; a new sorted dict is created. If other code holds a reference to the old dict, it still sees the unsorted version. The correct pattern is to sort and rebind, or to sort and keep the result as a list if you don't need key lookups.

⚠️ Common mistakes

The dict-sorting mistakes that cost real time.

1. d.sort()

AttributeError. Fix: sorted(d.items(), ...).

2. sorted(d, key=lambda x: x[1])

IndexError — sorted(d) yields keys (strings). Fix: sorted(d.items(), ...).

3. Expecting sorted() to return a dict

It returns a list. Fix: wrap with dict(...).

4. Using reverse=True for numbers only

Works for strings too. Fix: reverse=True is general.

5. Mixing value and key sorts

Confusing which index. Fix: item[0] = key, item[1] = value.

6. Negating strings

-x[1] fails for strings. Fix: reverse=True.

7. Expecting dict order to matter before 3.7

Old Python dicts were unordered. Fix: use OrderedDict or upgrade.

8. Sorting a dict with mixed-type values

TypeError. Fix: key=lambda x: str(x[1]) or explicit comparison.

❓ Frequently asked

How do I sort a dictionary by value in Python?

Use sorted with a key function: sorted(d.items(), key=lambda item: item[1]). This returns a list of (key, value) tuples, sorted by value ascending. Pass reverse=True for descending. To convert back to a dict: dict(sorted(d.items(), key=lambda item: item[1])).

Why does sorted() return a list, not a dict?

Because dictionaries aren't sortable — they're mappings, not sequences. sorted() works on any iterable and returns a list. For dicts, the natural iterable is .items(). If you want a dict back, wrap it: dict(sorted(d.items(), key=...)). Since Python 3.7, dicts preserve insertion order.

How do I sort a dictionary by key?

Use item[0] as the key: sorted(d.items(), key=lambda item: item[0]). Or, more simply, use sorted(d) — since iterating a dict yields its keys, sorted(d) returns a list of sorted keys. Another way: sorted(d.items()) — the default sort compares tuples lexicographically, so it sorts by key first.

What is operator.itemgetter and when should I use it?

operator.itemgetter(i) returns a callable that extracts the i-th element from its argument. It's a faster, more readable alternative to a lambda in sorting. sorted(d.items(), key=operator.itemgetter(1)) is equivalent to sorted(d.items(), key=lambda x: x[1]) but slightly faster. For most code, the lambda is fine.

How do I sort a dict by value in descending order?

Pass reverse=True: sorted(d.items(), key=lambda item: item[1], reverse=True). Alternatively, negate numeric values: sorted(d.items(), key=lambda item: -item[1]). But this only works for numeric values. For strings, use reverse=True. The reverse parameter is the cleanest and most general.

Can I sort a dict in place?

No. Dictionaries don't have a sort method. sorted() always returns a new list. To 'sort a dict in place', reassign: d = dict(sorted(d.items(), key=...)). The original dict is replaced by a new one. If other code holds a reference to the old dict, it still sees the old unsorted version.

🔗 Continue learning

🗺️ Python learning path
The full roadmap from install to first project — every concept in order

Next in this series:

🔢 How to iterate with an index
enumerate, zip, and why range(len()) is an anti-pattern
← All Python problems
Problem solved · Python