How do I sort a list of dictionaries by a value of the dictionary?

1 454

465

I have a list of dictionaries and want each item to be sorted by a specific property values.

Take into consideration the array below,

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

When sorted by name, should become

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

masi

Posted 2008-09-16T14:27:47.470

Reputation: 108

Answers

1 948

It may look cleaner using a key instead a cmp:

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

or as J.F.Sebastian and others suggested,

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

For completeness (as pointed out in comments by fitzgeraldsteele), add reverse=True to sort descending

newlist = sorted(l, key=itemgetter('name'), reverse=True)

Mario F

Posted 2008-09-16T14:27:47.470

Reputation: 26 207

7What would you change to make it sort descending? – NealWalters – 2009-10-13T04:14:16.170

3The 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 – 2009-10-13T07:14:38.427

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 – 2012-07-11T23:14:38.677

28itemgetter 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 – 2012-09-07T17:59:06.340

if the sort value is a number you could say:

lambda k: (k['age'] * -1)

to get a reverse sort – Philluminati – 2009-11-20T15:16:18.453

208To sort descending: newlist = sorted(l, key=itemgetter('name'), reverse=True) – fitzgeraldsteele – 2009-11-24T21:57:30.380

How could this be done if my dictionaries did not have a set key, but instead being structured more like a hashmap with the key being unique for each element. Is there a way of makeing lambda or itemgetter sorting on the value of the dictionary element wrapped inside the list? – olovholm – 2013-09-12T20:20:29.563

With do I do if I want to sort based on values of multiple keys? (This is basically the situation where some of the elements of the first key are same and I need the item to be sorted based on the following key) – Akash Kothawale – 2014-06-19T17:33:10.277

sort reverse with key: print sorted(newlist,key=lambda k: k['name'],reverse=True) – Stefan Gruenwald – 2014-11-03T20:06:03.387

As @radicand mentioned, you can use this info for lists of tuples, lists, or complex objects. The non-itemgetter version is newlist = sorted(list_to_be_sorted, key=lambda k: k[index]) or newlist = sorted(list_to_be_sorted, key=lambda k: k[index_integer]['subkey'][subindex_integer]) – Bruno Bronosky – 2016-05-18T19:54:16.183

Using my_list = [{"id":"1", "text":"Able"},{"id":"2", "text":"app-time"},{"id":"3", "text":"Red"}] in the above solutions, Red is placed before app-time. Is there anyway to ensure alphabetical sorting with hyphenated words? – user1063287 – 2016-07-27T15:24:54.517

17Using key is not only cleaner but more effecient too. – jfs – 2008-09-16T15:03:43.373

62lambda k: k['name'] could be replaced by operator.itemgetter('name'). – jfs – 2008-09-16T15:05:27.847

2If value for the corresponding key is None, it will throw exception TypeError: '<' not supported between instances of 'str' and 'NoneType'. How will you fix that with this solution? – Kanchan Srivastava – 2017-10-13T07:37:33.993

You're the best dude :) – Carlos Andres – 2018-10-12T18:43:01.277

118

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'))

vemury

Posted 2008-09-16T14:27:47.470

Reputation:

7Anyway to combine name and age ? (like in SQL ORDER BY name,age ?) – monojohnny – 2010-02-17T13:10:57.720

9what is the advantage of itemgetter over lambda ? – njzk2 – 2013-05-29T13:29:17.350

21@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 – 2013-09-04T22:21:15.933

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 – 2014-02-21T15:21:55.600

@monojohnny sorted(list_of_dicts, key=operator.itemgetter('name','age')) sorts the list of dictionaries by the name key then the age key. – IceArdor – 2014-09-05T01:23:48.793

1

@TTT: See the library documentation for list and friends.

– Kevin – 2015-02-19T14:56:53.033

44

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)

Dologan

Posted 2008-09-16T14:27:47.470

Reputation: 2 363

1sorted using timsort which is stable, you can call sorted several times to have a sort on several criteria – njzk2 – 2013-05-29T13:41:47.677

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 – 2013-08-23T21:05:24.547

12No need to convert to string. Just return a tuple as the key. – Winston Ewert – 2013-12-15T04:55:55.900

Sorting multiple times is the easiest generic solution without hacks: http://stackoverflow.com/a/29849371/1805397

– wouter bolsterlee – 2015-04-24T13:59:58.223

37

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.

(3 years later) Edited to add:

The new key argument is more efficient and neater. A better answer now looks like:

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

...the lambda is, IMO, easier to understand than operator.itemgetter, but YMMV.

pjz

Posted 2008-09-16T14:27:47.470

Reputation: 29 284

26

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

Posted 2008-09-16T14:27:47.470

Reputation: 11 756

18

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

Posted 2008-09-16T14:27:47.470

Reputation: 10 711

17

Using 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 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.

octoback

Posted 2008-09-16T14:27:47.470

Reputation: 13 615

6Python has supported the key= for .sort since 2.4, that is year 2004, it does the Schwartzian transform within the sorting code, in C; thus this method is useful only on Pythons 2.0-2.3. all of which are more than 12 years old. – Antti Haapala – 2015-02-15T20:11:29.490

15

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

Posted 2008-09-16T14:27:47.470

Reputation: 561

14

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

Posted 2008-09-16T14:27:47.470

Reputation: 598

14

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

Posted 2008-09-16T14:27:47.470

Reputation: 3 908

10

I tried something like this:

my_list.sort(key=lambda x: x['name'])

It worked for integers as well.

Sandip Agarwal

Posted 2008-09-16T14:27:47.470

Reputation: 1 195

9

Here is the alternative general solution - it sorts elements of 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)

vvladymyrov

Posted 2008-09-16T14:27:47.470

Reputation: 4 063

8

Using the pandas package is another method, though it's 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

abby sobh

Posted 2008-09-16T14:27:47.470

Reputation: 868

8

sometime we need to use lower() 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)
# [{'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}, {'name':'abby', 'age':9}]

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

uingtea

Posted 2008-09-16T14:27:47.470

Reputation: 861

5

Lets Say I h'v a Dictionary D with elements below. To sort just use key argument in sorted to pass 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

https://wiki.python.org/moin/HowTo/Sorting/#Key_Functions

Shank_Transformer

Posted 2008-09-16T14:27:47.470

Reputation: 133

4

Here is my answer to a related question on sorting by multiple columns. It also works for the degenerate case where the number of columns is only one.

hughdbrown

Posted 2008-09-16T14:27:47.470

Reputation: 30 367

2

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}]

Srisaila

Posted 2008-09-16T14:27:47.470

Reputation: 1 810

0

You may use the following code

sorted_dct = sorted(dct_name.items(), key = lambda x : x[1])

Saheli Mukherjee

Posted 2008-09-16T14:27:47.470

Reputation: 19