web-dev-qa-db-fra.com

Comment trier une liste de dictionnaires par une valeur du dictionnaire?

J'ai une liste de dictionnaires et je souhaite que chaque élément soit trié en fonction d'une valeur de propriété spécifique.

Prenez en considération le tableau ci-dessous,

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

Lorsque trié par name, devrait devenir

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

Il peut sembler plus propre d'utiliser une clé au lieu d'un cmp:

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

ou comme suggéré par J.F.Sebastian et d’autres,

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

Pour être complet (comme indiqué dans les commentaires de fitzgeraldsteele), ajoutez reverse=True pour trier par ordre décroissant.

newlist = sorted(l, key=itemgetter('name'), reverse=True)
2038
Mario F
import operator

Pour trier la liste des dictionnaires par clé = 'nom':

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

Pour trier la liste des dictionnaires par clé = 'âge':

list_of_dicts.sort(key=operator.itemgetter('age'))
123
vemury

Si vous souhaitez trier la liste par plusieurs clés, vous pouvez procéder comme suit:

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

Il est plutôt bidon, puisqu'il repose sur la conversion des valeurs en une représentation de chaîne unique pour la comparaison, mais il fonctionne comme prévu pour les nombres incluant les nombres négatifs (bien que vous ayez besoin de formater votre chaîne de manière appropriée avec zéro padding si vous utilisez des nombres)

43
Dologan
my_list = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

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

my_list sera maintenant ce que vous voulez.

(3 ans plus tard) Édité pour ajouter:

Le nouvel argument key est plus efficace et plus net. Une meilleure réponse ressemble maintenant à ceci:

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

... le lambda est, IMO, plus facile à comprendre que operator.itemgetter, mais YMMV.

38
pjz
import operator
a_list_of_dicts.sort(key=operator.itemgetter('name'))

'key' est utilisé pour trier selon une valeur arbitraire et 'itemgetter' définit cette valeur sur l'attribut 'name' de chaque élément.

26
efotinis

Je suppose que vous avez voulu dire:

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

Ce serait trié comme ceci:

sorted(l,cmp=lambda x,y: cmp(x['name'],y['name']))
18

En utilisant la transformation de Schwartzian de Perl,

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

faire

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

donne

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

Plus sur Perl Schwartzian transform

En informatique, la transformation de Schwartz est une programmation Perl idiome utilisé pour améliorer l’efficacité du tri d’une liste d’articles. Ce L'idiome est approprié pour le tri basé sur la comparaison lorsque l'ordre est réellement basé sur la commande d'une certaine propriété (la clé) de la éléments, où le calcul de cette propriété est une opération intensive que devrait être effectué un nombre minimal de fois. Le Schwartzian Transform est remarquable car il n'utilise pas de tableaux temporaires nommés.

17
octoback
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']) 
15
forzagreen

Vous pouvez utiliser une fonction de comparaison personnalisée ou transmettre une fonction qui calcule une clé de tri personnalisée. C’est généralement plus efficace, car la clé n’est calculée qu’une fois par article, alors que la fonction de comparaison serait appelée beaucoup plus souvent.

Vous pouvez le faire de cette façon:

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

Mais la bibliothèque standard contient une routine générique permettant d’obtenir des éléments d’objets arbitraires: itemgetter. Alors essayez ceci à la place:

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

Vous devez implémenter votre propre fonction de comparaison qui comparera les dictionnaires en fonction des valeurs des clés de nom. Voir Tri de Mini-HOW TO dans PythonInfo Wiki

14
Matej

J'ai essayé quelque chose comme ça:

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

Cela a également fonctionné pour les nombres entiers.

10
Sandip Agarwal

parfois nous avons besoin d'utiliser lower() par exemple

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

Voici la solution générale alternative - il trie les éléments de dict par clés et par valeurs ..__ L'avantage - inutile de spécifier des clés, et cela fonctionnerait encore s'il manquait des clés dans certains dictionnaires.

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)
9
vvladymyrov

Utiliser le paquetage pandas est une autre méthode, bien que son exécution à grande échelle soit beaucoup plus lente que les méthodes plus traditionnelles proposées par d'autres:

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

Voici quelques valeurs de référence pour une liste minuscule et une liste volumineuse (plus de 100 000 mots):

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
8
abby sobh

Disons que j'ai un dictionnaire D avec les éléments ci-dessous. Pour trier, il suffit d'utiliser l'argument de clé dans Trier pour passer la fonction personnalisée comme ci-dessous.

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

5
Shank_Transformer

Voici ma réponse à une question connexe sur le tri en plusieurs colonnes . Cela fonctionne également pour le cas dégénéré où le nombre de colonnes est unique.

4
hughdbrown

Si vous n'avez pas besoin de la list d'origine de dictionaries d'origine, vous pouvez la modifier sur place avec la méthode sort() à l'aide d'une fonction de clé personnalisée.

Fonction clef:

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

    return d["name"]

La list à trier:

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

Le trier sur place:

data_one.sort(key=get_name)

Si vous avez besoin de la list d'origine, appelez la fonction sorted() en lui passant la list et la fonction clé, puis affectez la list triée renvoyée à une nouvelle variable:

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

Impression data_one et new_data.

>>> print(data_one)
[{'name': 'Bart', 'age': 10}, {'name': 'Homer', 'age': 39}]
>>> print(new_data)
[{'name': 'Bart', 'age': 10}, {'name': 'Homer', 'age': 39}]
2
Srisaila

Vous pouvez utiliser itemgetter , si vous souhaitez prendre en compte les performances. itemgetter est généralement plus rapide que lambda .

from operator import itemgetter
result = sorted(data, key=itemgetter('age'))  # this will sort list by property order 'age'.
1
vikas0713

Vous pouvez utiliser le code suivant

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