Sort nested dicts in Python
by Antoine - categories : Programming Python
In this post, we will see how to sort one to many-levels-nested dictionnaries in CPython version >= 3.7 with the sorted()
function.
Starting with a simple 1-level dict, to 4-levels nested dict.
One level
I want to sort the following dict by the integer value. This is the simplest case, a dict with only one level.
dico = {
"l1_key1": 2,
"l1_key2": 1,
"l1_key3": 3
}
Solution
We call sorted()
with two arguments : what to move/sort, and the sorting key.
sorted(dico.items(), key=lambda x: x[1])
Details
What to sort/move :
dico.items()
-
Calling
.items()
returns keys and values of the dict. -
dico.items()
returns a list of tuples (actually its a "dict_items" object, but in our case it works like a list). Each tuple has 2 items : the key and the value.[('l1_key1', 2), ('l1_key2', 1), ('l1_key3', 3)]
- In this exemple, the key (position
[0]
of a tuple) is a string, and the value (position[1]
) is an integer
What is the sorting key :
key=lambda x: x[1]
- In the sorting
key
argument, we tell.sorted()
what data will be used to sort the.items()
tuples. x
is representing the tuples, and[1]
is the position in the tuples. Remember : a tuple has 2 elements : the key (position[0]
) and the value (position[1]
).- So,
x[1]
means that we want to sort by the value.
One more thing. Sorted will always return a list with keys as strings, and tuples as values. Just cast it to dict. The dict is our new-sorted dico
, so just update it.
sorted_dico = dict(sorted(dico.items(), key=lambda x:x[1]))
dico = sorted_dico
print(dico)
{
'l1_key2': 1,
'l1_key1': 2,
'l1_key3': 3
}
Two levels
I want to sort the following dict on Level1 keys, by the value of Level2 keys2. Scroll down to the end of this post to catch a glimpse at the wanted output
dico = {
"l1_k1": {
"l2_k1": "blabla",
"l2_k2": 1
},
"l1_k2": {
"l2_k1": "blabla",
"l2_k2": 2
},
"l1_k3": {
"l2_k1": "blabla",
"l2_k2": 3
}
}
Solution
sorted(dico.items(), key=lambda x: x[1]['l2_k2'])
Details
What to sort/move :
dico.items()
- We still want to reorder the dict at the first level keys.
- This time, the tuple returned by
dico.items()
is a bit different. The value (position[1]
) is now a dict since the first level keys contains another dict. [('l1_k1', {'l2_k1': 25, 'l2_k2': 2}), (...), (...)]
What is the sorting key :
key=lambda x: x[1]['l2_k2']
- Remember,
x[1]
is the "value" position in the tuples. In this case, as said above, its a dict. - Then it's pretty straightforward : since it's a dict, just move towards the wanted sorting key in the nested dict
['l2_k2']
- With 3 levels, we would have something like
key = lambda x: x[1]['l2_k2']['l3_k1']
, etc.
sorted_dico = dict(sorted(dico.items(),
key=lambda x:x[1]['l2_k2']))
dico = sorted_dico
print(dico)
Output :
{
'l1_k3': {
'l2_k1': "blabla",
'l2_k2': 1
},
'l1_k1': {
'l2_k1': "blabla",
'l2_k2': 2
},
'l1_k2': {
'l2_k1': "blabla",
'l2_k2': 3
}
}
Sort 2nd-level key by 4th-level value
A bit harder this time. I want to sort the following dict on Level2 keys, by the value of Level4 keys. If it's not clear in your mind, scroll down to the end to catch a glimpse at the wanted output.
dico = {
"l1_k1": {
"l2_k1": {
"l3_k1": "blabla",
"l3_k2": {
"l4_k1": "blabla",
"l4_k2": 3
}
},
"l2_k2": {
"l3_k1": "blabla",
"l3_k2": {
"l4_k1": "blabla",
"l4_k2": 1
}
},
"l2_k3": {
"l3_k1": "blabla",
"l3_k2": {
"l4_k1": "blabla",
"l4_k2": 2
}
}
},
"l1_k2": {"a key": "blabla"},
"l1_k3": {"a key": "blabla"}
}
Solution
sorted(dico['l1_k1'].items(),
key=lambda x: x[1]['l3_k2']['l4_k2']))
Details
What to sort/move :
dico['l1_k1'].items()
- The new thing is, we want datas inside l1_k1 dict to be sorted. Level 1 keys order will remain unchanged.
- The list of tuples returned shows keys + values inside l1_k1 :
[('l2_k1', {'l3_k1': 'blabla', 'l3_k2': {'l4_k1': 'blabla', 'l4_k2': 3}}), ('l2_k2', {...}), ('l2_k3', {...})]
What is the sorting key :
key=lambda x: x[1]['l3_k2']['l4_k2']
- Pretty straightforward now,
x[1]
still represents the values inside the tuples, and since it's a dict, we navigate towards the wanted sorting key l4_k2.
sorted_dico_l2 = dict(sorted(dico['l1_key'].items(),
key=lambda x: x[1]['l3_key2']['l4_key2']))
dico['l1_key'] = sorted_dico_l2
print(dico)
Output :
{
"l1_k1": {
"l2_k2": {
"l3_k1": "blabla",
"l3_k2": {
"l4_k1": "blabla",
"l4_k2": 1
}
},
"l2_k3": {
"l3_k1": "blabla",
"l3_k2": {
"l4_k1": "blabla",
"l4_k2": 2
}
},
"l2_k1": {
"l3_k1": "blabla",
"l3_k2": {
"l4_k1": "blabla",
"l4_k2": 3
}
}
},
"l1_k2": {"a key": "blabla"},
"l1_k3": {"a key": "blabla"}
}
More complex sorting key
A sorting function can be used as a key to return a more complex sorting. Let's say we want to sort some articles by creation date (cr_date), then by update date (up_date).
sorted(dico['l1_k1'].items(),
key=sorter)
def sorter(x):
cr_date = x[1]['cr_date']
up_date = x[1]['up_date']
return (cr_date, up_date)
Reverse sorting order
The .sorted()
function takes another useful optional argument called reverse
. Setting reverse=True
, well, reverse the sorting order. It is set by default to False.
Be the first to comment 🡮