Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild

Python Sort Dict by Key: sorted() Returns a List, Not a Dict

Sean

Platform Writer

Aug 17, 2026
8 min read

sorted(d) gives you a sorted list of the keys. sorted(d.items()) gives you a sorted list of key-value tuples. Neither is a dictionary. To get a dict back in key order you rebuild one — dict(sorted(d.items())) — which works because dictionaries have preserved insertion order since Python 3.7.

Python Sort Dict by Key: sorted() Returns a List, Not a Dict

That last detail is what changed this from an awkward problem into a one-liner. Before 3.7, dictionaries had no reliable order and OrderedDict was the only answer, which is why so much of the advice online still reaches for it. Today plain dicts remember the order you inserted things, so rebuilding one in sorted order does exactly what you want.

Table of contents

The three forms

d = {'watermelon': 1, 'apple': 2, 'banana': 3}

sorted(d)
# ['apple', 'banana', 'watermelon']  -- a list of keys

sorted(d.items())
# [('apple', 2), ('banana', 3), ('watermelon', 1)]  -- list of tuples

dict(sorted(d.items()))
# {'apple': 2, 'banana': 3, 'watermelon': 1}  -- a dict, in key order

Iterating a dict yields its keys, which is why sorted(d) sorts keys without you asking for them. It reads oddly the first time and is consistent with for k in d giving you keys.

dict(sorted(d.items())) is the form to remember. A dict comprehension is equivalent and some people find it clearer:

{k: d[k] for k in sorted(d)}
{k: v for k, v in sorted(d.items())}

All three build a new dictionary. There is no in-place sort for dicts, unlike list.sort(). If you need the original variable to hold the sorted version, reassign it.

For descending order, reverse=True on any of them.

When you only want to iterate

If the goal is to print or process in key order, do not build a dict at all.

for key in sorted(d):
    print(key, d[key])

for key, value in sorted(d.items()):
    print(key, value)

This avoids allocating a second dictionary. On a large mapping in a hot path that matters; on a config dict of twelve entries it does not, and clarity should win.

One thing to be careful of: sorted(d) inside a loop that also modifies d will raise, because you are mutating a dictionary while iterating its view. Materialise first with for key in sorted(list(d)) if you need to add or remove keys during the loop.

Custom sort orders

The key parameter takes a function applied to each element before comparison. This is where most real sorting problems get solved.

# case-insensitive
dict(sorted(d.items(), key=lambda item: item[0].lower()))

# by value instead of key
dict(sorted(d.items(), key=lambda item: item[1]))

# by value descending, then key ascending as a tiebreak
dict(sorted(d.items(), key=lambda item: (-item[1], item[0])))

# operator.itemgetter is slightly faster than a lambda
from operator import itemgetter
dict(sorted(d.items(), key=itemgetter(0)))

That third example is the pattern worth internalising: returning a tuple from the key function sorts by the first element, then the second for ties. Negating a number reverses just that field, which reverse=True cannot do because it flips everything.

For keys that are strings of digits, plain sorting is lexicographic and gives you '1', '10', '2'. Sort numerically instead:

dict(sorted(d.items(), key=lambda item: int(item[0])))

And for genuinely mixed content — 'item2', 'item10' — you need a natural sort key that splits digits from text, since neither plain nor integer sorting handles it.

Mixed key types will raise

A dict with both strings and integers as keys cannot be sorted with the default comparison.

d = {1: 'a', 'b': 2, 3: 'c'}
sorted(d)
# TypeError: '<' not supported between instances of 'str' and 'int'

Python 3 refuses to compare unrelated types, which is a deliberate improvement over Python 2 quietly ordering them by type name. The fix is a key function that produces a comparable value:

# sort everything as its string form
sorted(d, key=str)

# group by type first, then sort within each group
sorted(d, key=lambda k: (type(k).__name__, str(k)))

The second form keeps integers together and strings together, which is usually more useful output than interleaving them by string representation.

Worth pausing on if you hit this: a dictionary with mixed key types is often a sign that two different things are being stored in one mapping. Sorting is the symptom; the data model is the cause.

Nested dictionaries

Sorting only affects the top level. Nested dicts keep their own order untouched, which surprises people expecting a deep sort.

import json

def sort_deep(obj):
    if isinstance(obj, dict):
        return {k: sort_deep(obj[k]) for k in sorted(obj)}
    if isinstance(obj, list):
        return [sort_deep(i) for i in obj]
    return obj

That recurses through dicts and lists, sorting every dict it finds. Useful for producing stable output you want to diff — two structurally identical config files that differ only in key order produce a huge meaningless diff otherwise.

For JSON specifically there is a much simpler answer, since the standard library already has the flag:

json.dumps(data, sort_keys=True, indent=2)

sort_keys=True sorts recursively at every level during serialisation. If your goal is deterministic JSON output — for a diff, a checksum, or a snapshot test — that one argument is the whole solution and no manual recursion is needed.

OrderedDict, and when it still has a use

Since 3.7 plain dicts preserve insertion order as a language guarantee, so OrderedDict is no longer needed just to keep order. It still has three genuine uses.

  • move_to_end() — repositions a key to either end. This is what makes an LRU cache straightforward to implement.
  • Order-sensitive equality. Two OrderedDict objects with the same items in different orders compare unequal. Two plain dicts compare equal. Occasionally that distinction is exactly the check you want.
  • Explicit signalling. In code where order is load-bearing, the type name documents the intent in a way a comment does not.
from collections import OrderedDict

od = OrderedDict(sorted(d.items()))
od.move_to_end('apple')
od.move_to_end('banana', last=False)

Outside those cases, use a plain dict. It is faster, it prints more readably, and every modern Python maintains the order anyway. Reaching for OrderedDict because a Stack Overflow answer from 2012 said to is the common case, and it is no longer the right answer.

How this fits the rest of the stack

Sorting a dictionary is the sort of thing that behaves identically everywhere until the Python version underneath changes and a subtle ordering assumption stops holding. Pinning the runtime is what prevents that, and a build from a repository is where the pin gets enforced: the same interpreter version in the build and at runtime, with the build log attached to the deploy. Python applications deploy from GitHub on RunxBuild with build logs, a live route, environment variables, and rollback to the previous deploy — Python services on RunxBuild covers the runtime and build settings. When you are sizing a Python service alongside a managed Postgres or MySQL, the RunxBuild hosting calculator shows each part as its own line.

Useful related references:

FAQ

How do I sort a dictionary by key in Python?

Use dict(sorted(d.items())). sorted(d) alone returns a list of keys, not a dictionary, so you rebuild one from the sorted items. This works because dictionaries preserve insertion order as of Python 3.7.

Does sorted() return a dictionary?

No. sorted(d) returns a list of keys and sorted(d.items()) returns a list of key-value tuples. Wrap either in dict() or use a dict comprehension to get a dictionary back. There is no in-place sort for dicts.

How do I sort a dictionary case-insensitively?

Pass a key function that normalises case: dict(sorted(d.items(), key=lambda item: item[0].lower())). The key parameter is applied to each element before comparison and does not change the values stored.

Why do I get a TypeError sorting a dictionary?

The keys are of mixed types, and Python 3 refuses to compare unrelated types like str and int. Supply a key function producing comparable values — sorted(d, key=str), or key=lambda k: (type(k).__name__, str(k)) to group by type first.

Do I still need OrderedDict in modern Python?

Rarely. Plain dicts preserve insertion order since Python 3.7. OrderedDict remains useful for move_to_end(), which makes LRU caches simple, and for order-sensitive equality comparison. Otherwise a plain dict is faster and prints more readably.

#python sort dict by key#python dictionary#sorted#python#dict comprehension