2739

How do I sort a list of dictionaries by a specific key's value? Given:

[{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age': 10}]

When sorted by name, it should become:

[{'name': 'Bart', 'age': 10}, {'name': 'Homer', 'age': 39}]
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
masi
  • 118
  • 3
  • 5
  • 9
  • 6
    Reading the answer and looking on [operator.itemgetter](https://docs.python.org/3/library/operator.html#operator.itemgetter). Can I sort on multiple value in the same process (for example we have `[{'name':'Bart', 'age':10, 'note':3},{'name':'Homer','age':10,'note':2},{'name':'Vasile','age':20,'note':3}]` And to use: `from operator import itemgetter newlist = sorted(old_list, key=itemgetter(-'note','name')` EDIT: Tested, and it is working but I don't know how to make note DESC and name ASC. – Claudiu May 21 '20 at 07:13

22 Answers22

3584

The sorted() function takes a key= parameter

newlist = sorted(list_to_be_sorted, key=lambda d: d['name']) 

Alternatively, you can use operator.itemgetter instead of defining the function yourself

from operator import itemgetter
newlist = sorted(list_to_be_sorted, key=itemgetter('name')) 

For completeness, add reverse=True to sort in descending order

newlist = sorted(list_to_be_sorted, key=itemgetter('name'), reverse=True)
wjandrea
  • 28,235
  • 9
  • 60
  • 81
Mario F
  • 45,569
  • 6
  • 37
  • 38
  • 56
    Using key is not only cleaner but more effecient too. – jfs Sep 16 '08 at 15:03
  • 5
    The fastest way would be to add a newlist.reverse() statement. Otherwise you can define a comparison like cmp=lambda x,y: - cmp(x['name'],y['name']). – Mario F Oct 13 '09 at 07:14
  • 4
    if the sort value is a number you could say: lambda k: (k['age'] * -1) to get a reverse sort – Philluminati Nov 20 '09 at 15:16
  • 3
    This also applies to a list of tuples, if you use `itemgetter(i)` where `i` is the index of the tuple element to sort on. – radicand Jul 11 '12 at 23:14
  • 63
    `itemgetter` accepts more than one argument: `itemgetter(1,2,3)` is a function that return a tuple like `obj[1], obj[2], obj[3]`, so you can use it to do complex sorts. – Bakuriu Sep 07 '12 at 17:59
  • in case of unicode strings `import locale / locale.setlocale(locale.LC_ALL, "") / newlist = sorted(list_to_be_sorted, key=lambda k: locale.strxfrm(k['name']))` works inspired from https://stackoverflow.com/questions/1097908/how-do-i-sort-unicode-strings-alphabetically-in-python – webenformasyon Oct 15 '19 at 09:13
  • what if we don't know the value of key? – sattva_venu Jan 28 '21 at 12:53
  • operator.itemgetter worked for me, the first option didn't work or maybe I didn't fill in the right info... lambda? d: ? – Andrew Dec 30 '21 at 02:33
  • list indices must be integers or slices, not str – reza_khalafi Apr 04 '22 at 13:15
  • @Bakuriu is there any way to do complex sorts with both ascending and descending order? e.g. `key=itemgetter("speed", "color", "cost"), reverse=(True, False, True)` – ciurlaro Apr 06 '22 at 08:50
  • @ciurlaro Not that I know of. In order to make that work you'd first have to `map` the values in a way that make the fields you want sort in the opposite direction, then use a plain sort by those fields and at the end "un-wrap" the values. Obviously instead of using `itemgetter` you could use a custom function and do whatever you want there. – Bakuriu Apr 08 '22 at 12:01
  • 1
    @ciurlaro Note that in python sorting is stable, so say you want to achieve the equivalent of `key=itemgetter('A', 'B', 'C', 'D', 'E'), reverse=(True, True, False, True, True)` you could do that by sorting 3 times: first on `D, E` with `reverse=True`, then on `C` with `reverse=False` and finally on `A,B` with `reverse=True`. – Bakuriu Apr 08 '22 at 12:04
  • @Bakuriu I found this answer https://stackoverflow.com/a/56842689/3004162 that worked to achieve what I wanted. Still far from a beauty of a builtin function unfortunately, but that's the best I found – ciurlaro Apr 08 '22 at 15:38
  • I have a list of dict_keys containing months which are unfortunately unordered. How should I order them from january...to...december? thanks – Hip Aug 04 '22 at 20:03
  • @ciurlaro you might want to forget `operator.itemgetter` in that case and write out the accesses the "old-fashioned" way, negating the "color". `key=lambda x: (x["speed"], -x["color"], x["cost"])` – Chris Mar 22 '23 at 14:37
238
import operator

To sort the list of dictionaries by key='name':

list_of_dicts.sort(key=operator.itemgetter('name'))

To sort the list of dictionaries by key='age':

list_of_dicts.sort(key=operator.itemgetter('age'))
cedbeu
  • 1,919
  • 14
  • 24
  • 16
    Anyway to combine name and age ? (like in SQL ORDER BY name,age ?) – monojohnny Feb 17 '10 at 13:10
  • 48
    @monojohnny: yes, just have the key return a tuple, `key=lambda k: (k['name'], k['age'])`. (or `key=itemgetter('name', 'age')`). tuple's `cmp` will compare each element in turn. it's bloody brilliant. – Claudiu Sep 04 '13 at 22:21
  • 1
    In the documentation (http://docs.python.org/2/tutorial/datastructures.html) the optional `key` argument for `list.sort()` is not described. Any idea where to find that? – TTT Feb 21 '14 at 15:21
  • 2
    @TTT: See the [library documentation](https://docs.python.org/2/library/stdtypes.html#mutable-sequence-types) for `list` and friends. – Kevin Feb 19 '15 at 14:56
119
my_list = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

my_list.sort(lambda x,y : cmp(x['name'], y['name']))

my_list will now be what you want.

Or better:

Since Python 2.4, there's a key argument is both more efficient and neater:

my_list = sorted(my_list, key=lambda k: k['name'])

...the lambda is, IMO, easier to understand than operator.itemgetter, but your mileage may vary.

pjz
  • 41,842
  • 6
  • 48
  • 60
  • what could be done if the key is unknown and keeps changing?I mean list of dicts with just one key and value but the key and value could not be defined as they keep changing. – Sam Dec 01 '20 at 14:51
  • 1
    I'd need more of an example to look at. Try submitting a possible solution on the codereview stackexchange and asking if there's a better way. – pjz Dec 30 '20 at 01:02
  • 1
    @Sam if you want to sort by the value of the single key in the dict, even if you don't know the key, you can do `key=lambda k: list(k.values())[0]` – pjz Mar 10 '21 at 06:38
75

If you want to sort the list by multiple keys, you can do the following:

my_list = [{'name':'Homer', 'age':39}, {'name':'Milhouse', 'age':10}, {'name':'Bart', 'age':10} ]
sortedlist = sorted(my_list , key=lambda elem: "%02d %s" % (elem['age'], elem['name']))

It is rather hackish, since it relies on converting the values into a single string representation for comparison, but it works as expected for numbers including negative ones (although you will need to format your string appropriately with zero paddings if you are using numbers).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dologan
  • 4,554
  • 2
  • 31
  • 33
  • 3
    sorted using timsort which is stable, you can call sorted several times to have a sort on several criteria – njzk2 May 29 '13 at 13:41
  • 1
    njzk2's comment wasn't immediately clear to me so I found the following. You can just sort twice as njzk2 suggests, or pass multiple arguments to operator.itemgetter in the top answer. Link: http://stackoverflow.com/questions/5212870/sorting-a-python-list-by-two-criteria – Permafacture Aug 23 '13 at 21:05
  • 18
    No need to convert to string. Just return a tuple as the key. – Winston Ewert Dec 15 '13 at 04:55
  • Sorting multiple times is the easiest generic solution without hacks: http://stackoverflow.com/a/29849371/1805397 – wouter bolsterlee Apr 24 '15 at 13:59
52
a = [{'name':'Homer', 'age':39}, ...]

# This changes the list a
a.sort(key=lambda k : k['name'])

# This returns a new list (a is not modified)
sorted(a, key=lambda k : k['name']) 
forzagreen
  • 2,509
  • 30
  • 38
42
import operator
a_list_of_dicts.sort(key=operator.itemgetter('name'))

'key' is used to sort by an arbitrary value and 'itemgetter' sets that value to each item's 'name' attribute.

efotinis
  • 14,565
  • 6
  • 31
  • 36
32

I guess you've meant:

[{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

This would be sorted like this:

sorted(l,cmp=lambda x,y: cmp(x['name'],y['name']))
Bartosz Radaczyński
  • 18,396
  • 14
  • 54
  • 61
28

Sometime we need to use lower() for case-insensitive sorting. For example,

lists = [{'name':'Homer', 'age':39},
  {'name':'Bart', 'age':10},
  {'name':'abby', 'age':9}]

lists = sorted(lists, key=lambda k: k['name'])
print(lists)
# Bart, Homer, abby
# [{'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}, {'name':'abby', 'age':9}]

lists = sorted(lists, key=lambda k: k['name'].lower())
print(lists)
# abby, Bart, Homer
# [ {'name':'abby', 'age':9}, {'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}]
uingtea
  • 6,002
  • 2
  • 26
  • 40
  • *Why* do we need to use lower() in this case? – Peter Mortensen Aug 14 '20 at 20:52
  • 2
    The most likely reason to need to use `lower()` here would be to provide case-insensitive alphabetical sorting. This sample dataset has a lower-case a with _abby_ and an upper-case B with _Bart_, so the examples show the results without, and then with, case-insensitive sort via `.lower()`. – Joel Aufrecht Dec 23 '22 at 21:40
28

You could use a custom comparison function, or you could pass in a function that calculates a custom sort key. That's usually more efficient as the key is only calculated once per item, while the comparison function would be called many more times.

You could do it this way:

def mykey(adict): return adict['name']
x = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age':10}]
sorted(x, key=mykey)

But the standard library contains a generic routine for getting items of arbitrary objects: itemgetter. So try this instead:

from operator import itemgetter
x = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age':10}]
sorted(x, key=itemgetter('name'))
Owen
  • 891
  • 6
  • 6
27

Using the Schwartzian transform from Perl,

py = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

do

sort_on = "name"
decorated = [(dict_[sort_on], dict_) for dict_ in py]
decorated.sort()
result = [dict_ for (key, dict_) in decorated]

gives

>>> result
[{'age': 10, 'name': 'Bart'}, {'age': 39, 'name': 'Homer'}]

More on the Perl Schwartzian transform:

In computer science, the Schwartzian transform is a Perl programming idiom used to improve the efficiency of sorting a list of items. This idiom is appropriate for comparison-based sorting when the ordering is actually based on the ordering of a certain property (the key) of the elements, where computing that property is an intensive operation that should be performed a minimal number of times. The Schwartzian Transform is notable in that it does not use named temporary arrays.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
kiriloff
  • 25,609
  • 37
  • 148
  • 229
24

You have to implement your own comparison function that will compare the dictionaries by values of name keys. See Sorting Mini-HOW TO from PythonInfo Wiki

Matej
  • 6,004
  • 2
  • 28
  • 27
20

Using the Pandas package is another method, though its runtime at large scale is much slower than the more traditional methods proposed by others:

import pandas as pd

listOfDicts = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]
df = pd.DataFrame(listOfDicts)
df = df.sort_values('name')
sorted_listOfDicts = df.T.to_dict().values()

Here are some benchmark values for a tiny list and a large (100k+) list of dicts:

setup_large = "listOfDicts = [];\
[listOfDicts.extend(({'name':'Homer', 'age':39}, {'name':'Bart', 'age':10})) for _ in range(50000)];\
from operator import itemgetter;import pandas as pd;\
df = pd.DataFrame(listOfDicts);"

setup_small = "listOfDicts = [];\
listOfDicts.extend(({'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}));\
from operator import itemgetter;import pandas as pd;\
df = pd.DataFrame(listOfDicts);"

method1 = "newlist = sorted(listOfDicts, key=lambda k: k['name'])"
method2 = "newlist = sorted(listOfDicts, key=itemgetter('name')) "
method3 = "df = df.sort_values('name');\
sorted_listOfDicts = df.T.to_dict().values()"

import timeit
t = timeit.Timer(method1, setup_small)
print('Small Method LC: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup_small)
print('Small Method LC2: ' + str(t.timeit(100)))
t = timeit.Timer(method3, setup_small)
print('Small Method Pandas: ' + str(t.timeit(100)))

t = timeit.Timer(method1, setup_large)
print('Large Method LC: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup_large)
print('Large Method LC2: ' + str(t.timeit(100)))
t = timeit.Timer(method3, setup_large)
print('Large Method Pandas: ' + str(t.timeit(1)))

#Small Method LC: 0.000163078308105
#Small Method LC2: 0.000134944915771
#Small Method Pandas: 0.0712950229645
#Large Method LC: 0.0321750640869
#Large Method LC2: 0.0206089019775
#Large Method Pandas: 5.81405615807
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
abby sobh
  • 1,574
  • 19
  • 15
  • 4
    I ran your code and found a mistake in the the timeit.Timer args for Large Method Pandas: you specify "setup_small" where it should be "setup_large". Changing that arg caused the program to run without finishing, and I stopped it after more than 5 minutes. When I ran it with "timeit(1)", the Large Method Pandas finished in 7.3 sec, much worse than LC or LC2. – clp2 Nov 07 '16 at 04:05
  • You're quite right, that was quite an oversight on my part. I no longer recommend it for large cases! I have edited the answer to simply allow it as a possibility, the use case is still up for debate. – abby sobh Nov 08 '16 at 22:58
17

Here is the alternative general solution - it sorts elements of a dict by keys and values.

The advantage of it - no need to specify keys, and it would still work if some keys are missing in some of dictionaries.

def sort_key_func(item):
    """ Helper function used to sort list of dicts

    :param item: dict
    :return: sorted list of tuples (k, v)
    """
    pairs = []
    for k, v in item.items():
        pairs.append((k, v))
    return sorted(pairs)
sorted(A, key=sort_key_func)
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
vvladymyrov
  • 5,715
  • 2
  • 32
  • 50
16

I have been a big fan of a filter with lambda. However, it is not best option if you consider time complexity.

First option

sorted_list = sorted(list_to_sort, key= lambda x: x['name'])
# Returns list of values

Second option

list_to_sort.sort(key=operator.itemgetter('name'))
# Edits the list, and does not return a new list

Fast comparison of execution times

# First option
python3.6 -m timeit -s "list_to_sort = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}, {'name':'Faaa', 'age':57}, {'name':'Errr', 'age':20}]" -s "sorted_l=[]" "sorted_l = sorted(list_to_sort, key=lambda e: e['name'])"

1000000 loops, best of 3: 0.736 µsec per loop

# Second option
python3.6 -m timeit -s "list_to_sort = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}, {'name':'Faaa', 'age':57}, {'name':'Errr', 'age':20}]" -s "sorted_l=[]" -s "import operator" "list_to_sort.sort(key=operator.itemgetter('name'))"

1000000 loops, best of 3: 0.438 µsec per loop

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Bejür
  • 110
  • 2
  • 7
15

Let's say I have a dictionary D with the elements below. To sort, just use the key argument in sorted to pass a custom function as below:

D = {'eggs': 3, 'ham': 1, 'spam': 2}
def get_count(tuple):
    return tuple[1]

sorted(D.items(), key = get_count, reverse=True)
# Or
sorted(D.items(), key = lambda x: x[1], reverse=True)  # Avoiding get_count function call

Check this out.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
13

If you do not need the original list of dictionaries, you could modify it in-place with sort() method using a custom key function.

Key function:

def get_name(d):
    """ Return the value of a key in a dictionary. """

    return d["name"]

The list to be sorted:

data_one = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age': 10}]

Sorting it in-place:

data_one.sort(key=get_name)

If you need the original list, call the sorted() function passing it the list and the key function, then assign the returned sorted list to a new variable:

data_two = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age': 10}]
new_data = sorted(data_two, key=get_name)

Printing data_one and new_data.

>>> print(data_one)
[{'name': 'Bart', 'age': 10}, {'name': 'Homer', 'age': 39}]
>>> print(new_data)
[{'name': 'Bart', 'age': 10}, {'name': 'Homer', 'age': 39}]
srikavineehari
  • 2,502
  • 1
  • 11
  • 21
11

If performance is a concern, I would use operator.itemgetter instead of lambda as built-in functions perform faster than hand-crafted functions. The itemgetter function seems to perform approximately 20% faster than lambda based on my testing.

From https://wiki.python.org/moin/PythonSpeed:

Likewise, the builtin functions run faster than hand-built equivalents. For example, map(operator.add, v1, v2) is faster than map(lambda x,y: x+y, v1, v2).

Here is a comparison of sorting speed using lambda vs itemgetter.

import random
import operator

# Create a list of 100 dicts with random 8-letter names and random ages from 0 to 100.
l = [{'name': ''.join(random.choices(string.ascii_lowercase, k=8)), 'age': random.randint(0, 100)} for i in range(100)]

# Test the performance with a lambda function sorting on name
%timeit sorted(l, key=lambda x: x['name'])
13 µs ± 388 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

# Test the performance with itemgetter sorting on name
%timeit sorted(l, key=operator.itemgetter('name'))
10.7 µs ± 38.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

# Check that each technique produces the same sort order
sorted(l, key=lambda x: x['name']) == sorted(l, key=operator.itemgetter('name'))
True

Both techniques sort the list in the same order (verified by execution of the final statement in the code block), but the first one is a little faster.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
swac
  • 51
  • 1
  • 4
5

You can sort a list of dictionaries with a key as shown below:

person_list = [
  {'name':'Bob','age':18}, {'name':'Kai','age':36}, {'name':'Ada','age':24} 
]
                                       # Key ↓
print(sorted(person_list, key=lambda x: x['name']))

Output:

[
  {'name':'Ada','age':24}, {'name':'Bob','age':18}, {'name':'Kai','age':36}
]

In addition, you can sort a list of dictionaries with a key and a list of values as shown below:

person_list = [
  {'name':'Bob','age':18}, {'name':'Kai','age':36}, {'name':'Ada','age':24} 
]

name_list = ['Kai', 'Ada', 'Bob'] # Here
                                      # ↓ Here ↓       # Key ↓
print(sorted(person_list, key=lambda x: name_list.index(x['name'])))

Output:

[
  {'name':'Kai', 'age':36}, {'name':'Ada', 'age':24}, {'name':'Bob','age':18}
]
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
4

It might be better to use dict.get() to fetch the values to sort by in the sorting key. One way it's better than dict[] is that a default value may be used to if a key is missing in some dictionary in the list.

For example, if a list of dicts were sorted by 'age' but 'age' was missing in some dict, that dict can either be pushed to the back of the sorted list (or to the front) by simply passing inf as a default value to dict.get().

lst = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age': 10}, {'name': 'Lisa'}]

sorted(lst, key=lambda d: d['age'])                     # KeyError: 'age'
sorted(lst, key=itemgetter('age'))                      # KeyError: 'age'

# push dicts with missing keys to the back
sorted(lst, key=lambda d: d.get('age', float('inf')))   # OK
# push dicts with missing keys to the front
sorted(lst, key=lambda d: d.get('age', -float('inf')))  # OK

# if the value to be sorted by is a string
# '~' because it has the highest printable ASCII value
sorted(lst, key=lambda d: d.get('name', '~'))           # OK  
cottontail
  • 10,268
  • 18
  • 50
  • 51
2

As indicated by @Claudiu to @monojohnny in comment section of this answer,
given:

list_to_be_sorted = [
                      {'name':'Homer', 'age':39}, 
                      {'name':'Milhouse', 'age':10}, 
                      {'name':'Bart', 'age':10} 
                    ]

to sort the list of dictionaries by key 'age', 'name'
(like in SQL statement ORDER BY age, name), you can use:

newlist = sorted( list_to_be_sorted, key=lambda k: (k['age'], k['name']) )

or, likewise

import operator
newlist = sorted( list_to_be_sorted, key=operator.itemgetter('age','name') )

print(newlist)

[{'name': 'Bart', 'age': 10},
{'name': 'Milhouse', 'age': 10},
{'name': 'Homer', 'age': 39}]

Tms91
  • 3,456
  • 6
  • 40
  • 74
1

sorting by multiple columns, while in descending order on some of them: the cmps array is global to the cmp function, containing field names and inv == -1 for desc 1 for asc

def cmpfun(a, b):
    for (name, inv) in cmps:
        res = cmp(a[name], b[name])
        if res != 0:
            return res * inv
    return 0

data = [
    dict(name='alice', age=10), 
    dict(name='baruch', age=9), 
    dict(name='alice', age=11),
]

all_cmps = [
    [('name', 1), ('age', -1)], 
    [('name', 1), ('age', 1)], 
    [('name', -1), ('age', 1)],]

print 'data:', data
for cmps in all_cmps: print 'sort:', cmps; print sorted(data, cmpfun)
alex
  • 651
  • 1
  • 9
  • 11
1

You can use the following:

lst = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age': 10}]
sorted_lst = sorted(lst, key=lambda x: x['age']) # change this to sort by a different field
print(sorted_lst)
Manrique
  • 2,083
  • 3
  • 15
  • 38