¿Cómo clasifico una lista de diccionarios por un valor del diccionario?
Frecuentes
Visto 841,057 equipos
2126
Tengo una lista de diccionarios y quiero que cada elemento se ordene por un valor específico.
Tenga en cuenta la lista:
[{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]
Cuando se ordena por name
, debería convertirse en:
[{'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}]
17 Respuestas
2723
Puede parecer más limpio usando una clave en lugar de un cmp:
newlist = sorted(list_to_be_sorted, key=lambda k: k['name'])
o como sugirieron JFSebastian y otros,
from operator import itemgetter
newlist = sorted(list_to_be_sorted, key=itemgetter('name'))
Para completar (como se señaló en los comentarios de fitzgeraldsteele), agregue reverse=True
ordenar descendente
newlist = sorted(l, key=itemgetter('name'), reverse=True)
Respondido el 31 de diciembre de 14 a las 19:12
Usar la clave no solo es más limpio sino también más eficiente. - jfs
La forma más rápida sería agregar una declaración newlist.reverse (). De lo contrario, puede definir una comparación como cmp = lambda x, y: - cmp (x ['nombre'], y ['nombre']). - mario f
si el valor de clasificación es un número, podría decir: lambda k: (k ['edad'] * -1) para obtener una clasificación inversa - Philluminati
Esto también se aplica a una lista de tuplas, si usa itemgetter(i)
donde i
es el índice del elemento de tupla para ordenar. - radicando
itemgetter
acepta más de un argumento: itemgetter(1,2,3)
es una función que devuelve una tupla como obj[1], obj[2], obj[3]
, por lo que puede utilizarlo para realizar ordenaciones complejas. - Bakuriú
183
import operator
Para ordenar la lista de diccionarios por clave = 'nombre':
list_of_dicts.sort(key=operator.itemgetter('name'))
Para ordenar la lista de diccionarios por clave = 'edad':
list_of_dicts.sort(key=operator.itemgetter('age'))
contestado el 01 de mayo de 13 a las 15:05
De todos modos, ¿combinar nombre y edad? (como en SQL ORDER BY nombre, edad?) - monojohnny
@monojohnny: sí, solo haz que la clave devuelva una tupla, key=lambda k: (k['name'], k['age'])
. (o key=itemgetter('name', 'age')
). tupla cmp
comparará cada elemento a su vez. es malditamente brillante. - Claudiu
En la documentación (docs.python.org/2/tutorial/datastructures.html) el opcional key
argumento a favor list.sort()
no se describe. ¿Alguna idea de dónde encontrar eso? - TTT
@TTT: Ver el documentación de la biblioteca en list
y amigos. - Kevin
78
my_list = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]
my_list.sort(lambda x,y : cmp(x['name'], y['name']))
my_list
ahora será lo que quieras.
O mejor:
Desde Python 2.4, hay una key
El argumento es más eficiente y ordenado:
my_list = sorted(my_list, key=lambda k: k['name'])
... la lambda es, en mi opinión, más fácil de entender que operator.itemgetter
, pero tu kilometraje puede variar.
Respondido el 25 de Septiembre de 20 a las 16:09
¿Qué se podría hacer si la clave es desconocida y sigue cambiando? Me refiero a una lista de dictados con solo una clave y un valor, pero la clave y el valor no se pueden definir porque siguen cambiando. - Diana
Necesitaría más ejemplos para mirar. Intente enviar una posible solución en codereview stackexchange y pregunte si hay una mejor manera. - PJZ
55
Si desea ordenar la lista por varias claves, puede hacer lo siguiente:
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']))
Es bastante pirateado, ya que se basa en convertir los valores en una representación de una sola cadena para la comparación, pero funciona como se espera para los números, incluidos los negativos (aunque deberá formatear su cadena de manera adecuada con relleno de ceros si está usando números).
Respondido 14 ago 20, 21:08
ordenado usando timsort que es estable, puede llamar ordenado varias veces para tener una ordenación según varios criterios: njzk2
El comentario de njzk2 no me quedó claro de inmediato, así que encontré lo siguiente. Puede ordenar dos veces como sugiere njzk2, o pasar varios argumentos a operator.itemgetter en la respuesta superior. Enlace: stackoverflow.com/questions/5212870/… - Permafactura
No es necesario convertir a cadena. Solo devuelve una tupla como clave. - winston ewert
Ordenar varias veces es la solución genérica más sencilla sin trucos: stackoverflow.com/a/29849371/1805397 - exterior reforzado
30
import operator
a_list_of_dicts.sort(key=operator.itemgetter('name'))
'clave' se usa para ordenar por un valor arbitrario y 'itemgetter' establece ese valor en el atributo 'nombre' de cada elemento.
Respondido el 16 de Septiembre de 08 a las 15:09
30
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'])
respondido 17 mar '17, 10:03
22
Supongo que te refieres a:
[{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]
Esto se ordenaría así:
sorted(l,cmp=lambda x,y: cmp(x['name'],y['name']))
Respondido el 16 de Septiembre de 08 a las 15:09
20
Puede utilizar una función de comparación personalizada o puede pasar una función que calcule una clave de clasificación personalizada. Eso suele ser más eficiente ya que la clave solo se calcula una vez por elemento, mientras que la función de comparación se llamaría muchas más veces.
Podrías hacerlo de esta manera:
def mykey(adict): return adict['name']
x = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age':10}]
sorted(x, key=mykey)
Pero la biblioteca estándar contiene una rutina genérica para obtener elementos de objetos arbitrarios: itemgetter
. Así que prueba esto en su lugar:
from operator import itemgetter
x = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age':10}]
sorted(x, key=itemgetter('name'))
Respondido el 16 de Septiembre de 08 a las 15:09
20
Usando el Transformada de Schwartzian de 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]
da
>>> result
[{'age': 10, 'name': 'Bart'}, {'age': 39, 'name': 'Homer'}]
Más sobre la transformación de Perl Schwartzian:
En ciencias de la computación, la transformada de Schwartzian es un lenguaje de programación de Perl que se usa para mejorar la eficiencia de ordenar una lista de elementos. Este lenguaje es apropiado para la ordenación basada en comparación cuando la ordenación se basa realmente en la ordenación de una determinada propiedad (la clave) de los elementos, donde calcular esa propiedad es una operación intensiva que debe realizarse un número mínimo de veces. La Transformada de Schwartzian es notable porque no utiliza matrices temporales con nombre.
Respondido 14 ago 20, 21:08
Python ha apoyado el key=
en .sort
desde 2.4, que es el año 2004, hace la transformación de Schwartzian dentro del código de clasificación, en C; por lo tanto, este método solo es útil en Pythons 2.0-2.3. todos los cuales tienen más de 12 años. - antti haapala
18
Debe implementar su propia función de comparación que comparará los diccionarios por valores de claves de nombre. Ver Ordenando Mini-CÓMO DE PythonInfo Wiki
Respondido el 16 de Septiembre de 08 a las 15:09
Esto depende demasiado del enlace. ¿Puede dar una respuesta más completa? - Pedro Mortensen
Otros colaboradores ya han proporcionado respuestas adecuadas. Siéntase libre de mantener el enlace o eliminar la respuesta. - Matej
14
A veces necesitamos usar lower()
. Por ejemplo,
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}]
Respondido 22 Jul 20, 14:07
¿Por qué ¿Necesitamos usar lower () en este caso? - Pedro Mortensen
11
Aquí está la solución general alternativa: ordena los elementos de un dictado por claves y valores.
La ventaja de esto: no es necesario especificar claves, y aún funcionaría si faltan algunas claves en algunos de los diccionarios.
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)
Respondido 14 ago 20, 21:08
Que quieres decir con "ordena los elementos de un dictado por claves y valores"? ¿De qué manera se clasifica? ¿Dónde entran los valores? - Pedro Mortensen
10
Usando el pandas package es otro método, aunque su tiempo de ejecución a gran escala es mucho más lento que los métodos más tradicionales propuestos por otros:
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()
Aquí hay algunos valores de referencia para una lista pequeña y una lista grande (100k +) de dictados:
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
Respondido 22 Jul 20, 14:07
Ejecuté su código y encontré un error en el timeit.Timer args para Large Method Pandas: usted especifica "setup_small" donde debería estar "setup_large". Cambiar ese argumento hizo que el programa se ejecutara sin terminar y lo detuve después de más de 5 minutos. Cuando lo ejecuté con "timeit (1)", Large Method Pandas terminó en 7.3 segundos, mucho peor que LC o LC2. - clp2
Tienes toda la razón, eso fue un gran descuido de mi parte. ¡Ya no lo recomiendo para casos grandes! He editado la respuesta para permitirlo simplemente como una posibilidad, el caso de uso aún está en debate. - abby sollozo
7
Digamos que tengo un diccionario D
con los elementos siguientes. Para ordenar, simplemente use el argumento clave en sorted
para pasar una función personalizada como se muestra a continuación:
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
Revise este fuera.
Respondido 14 ago 20, 21:08
6
Si no necesita el original list
of dictionaries
, puede modificarlo in situ con sort()
método utilizando una función de tecla personalizada.
Función de la tecla:
def get_name(d):
""" Return the value of a key in a dictionary. """
return d["name"]
La list
a ordenar:
data_one = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age': 10}]
Ordenarlo en el lugar:
data_one.sort(key=get_name)
Si necesitas el original list
, llama a sorted()
función pasándole el list
y la función de la tecla, luego asigne la orden devuelta list
a una nueva variable:
data_two = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age': 10}]
new_data = sorted(data_two, key=get_name)
Impresión data_one
y new_data
.
>>> print(data_one)
[{'name': 'Bart', 'age': 10}, {'name': 'Homer', 'age': 39}]
>>> print(new_data)
[{'name': 'Bart', 'age': 10}, {'name': 'Homer', 'age': 39}]
Respondido el 19 de diciembre de 17 a las 17:12
5
He sido un gran admirador de un filtro con lambda. Sin embargo, no es la mejor opción si considera la complejidad del tiempo.
Primera opción
sorted_list = sorted(list_to_sort, key= lambda x: x['name'])
# Returns list of values
Segunda opción
list_to_sort.sort(key=operator.itemgetter('name'))
# Edits the list, and does not return a new list
Comparación rápida de tiempos de ejecución
# 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 bucles, lo mejor de 3: 0.736 µseg por bucle
# 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 bucles, lo mejor de 3: 0.438 µseg por bucle
Respondido 14 ago 20, 22:08
4
Si el rendimiento es una preocupación, usaría operator.itemgetter
en lugar de lambda
ya que las funciones integradas funcionan más rápido que las funciones creadas a mano. La itemgetter
La función parece funcionar aproximadamente un 20% más rápido que lambda
basado en mis pruebas.
Desde https://wiki.python.org/moin/PythonSpeed:
Del mismo modo, las funciones integradas se ejecutan más rápido que los equivalentes construidos a mano. Por ejemplo, map (operator.add, v1, v2) es más rápido que map (lambda x, y: x + y, v1, v2).
Aquí hay una comparación de la velocidad de clasificación usando 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
Ambas técnicas ordenan la lista en el mismo orden (verificado mediante la ejecución de la declaración final en el bloque de código), pero la primera es un poco más rápida.
Respondido 14 ago 20, 22:08
No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas python list sorting dictionary data-structures or haz tu propia pregunta.
Leer la respuesta y mirar operador.itemgetter. ¿Puedo ordenar varios valores en el mismo proceso (por ejemplo, tenemos
[{'name':'Bart', 'age':10, 'note':3},{'name':'Homer','age':10,'note':2},{'name':'Vasile','age':20,'note':3}]
Y para usar:from operator import itemgetter newlist = sorted(old_list, key=itemgetter(-'note','name')
EDITAR: Probado y está funcionando, pero no sé cómo anotar DESC y nombrar ASC. - Claudiu