2013-04-18 31 views
59

Desidero ordinare una lista inizialmente con un valore e poi con un secondo valore. C'è un modo semplice per farlo? Ecco un piccolo esempio:Python: come ordinare un elenco di dizionari in base a diversi valori?

A = [{'name':'john','age':45}, 
    {'name':'andi','age':23}, 
    {'name':'john','age':22}, 
    {'name':'paul','age':35}, 
    {'name':'john','age':21}] 

Questo comando è per l'ordinamento questa lista 'name':

sorted(A, key = lambda user: user['name']) 

Ma come posso ordinare questa lista da un secondo valore? Come 'age' in questo esempio.

Voglio un ordinamento come questo (Ordina per 'name' e poi ordina per 'age'):

andi - 23 
john - 21 
john - 22 
john - 45 
paul - 35 

Grazie!

+5

Su un lato non: sorta di Python è garantita * * a essere stabile, in tal modo si può semplicemente 'sort' da' age' e poi da 'name' per ottenere il risultato che si voleva. (nota che i tasti sono in ordine inverso, per prima cosa devi ordinare la seconda chiave e poi la prima). – Bakuriu

risposta

85
>>> A = [{'name':'john','age':45}, 
    {'name':'andi','age':23}, 
    {'name':'john','age':22}, 
    {'name':'paul','age':35}, 
    {'name':'john','age':21}] 
>>> sorted(A, key = lambda user: (user['name'], user['age'])) 
[{'age': 23, 'name': 'andi'}, {'age': 21, 'name': 'john'}, {'age': 22, 'name': 'john'}, {'age': 45, 'name': 'john'}, {'age': 35, 'name': 'paul'}] 

Questa sorta di una tupla dei due attributi, il seguente è equivalente e molto più veloce/più pulito:

>>> from operator import itemgetter 
>>> sorted(A, key=itemgetter('name', 'age')) 
[{'age': 23, 'name': 'andi'}, {'age': 21, 'name': 'john'}, {'age': 22, 'name': 'john'}, {'age': 45, 'name': 'john'}, {'age': 35, 'name': 'paul'}] 

Dai commenti: @Bakuriu

Scommetto c'è non una grande differenza tra i due, ma itemgetter evita un po 'di overhead perché estrae le chiavi e rende il tuple durante un unico codice operativo (CALL_FUNCTION), mentre chiama lo lambda dovrà chiamare la funzione, caricare le varie costanti (che sono altri bytecode) infine chiamare il pedice (BINARY_SUBSCR), creare il tuple e restituirlo ... è molto più lavoro per l'interprete.

In sintesi: itemgetter mantiene l'esecuzione completamente sul piano C, quindi è il più velocemente possibile.

+3

Sarei interessato a una spiegazione * perché * itemgetter sarebbe molto più veloce dell'espressione lambda. Non si riduce alle stesse ricerche? – catchmeifyoutry

+4

@catchmeifyoutry Scommetto che non c'è una differenza * grande * tra i due, ma 'itemgetter' evita un po 'di overhead perché estrae le chiavi e crea la tupla durante un singolo opcode (' CALL_FUNCTION'), mentre chiama il lambda devo chiamare la funzione, caricare le varie costanti (che sono altri bytecode) infine chiamare il pedice ('BINARY_SUBSCR'), costruire la tupla e restituirla ... questo è molto più lavoro per l'interprete – Bakuriu

+0

@Bakuriu Grazie, per il spiegazione. Quindi l'implementazione itemgetter è ottimizzata in cpython come codice c, non solo implementata come il codice python di riferimento menzionato nella sua documentazione online. – catchmeifyoutry

52
from operator import itemgetter 

sorted(your_list, key=itemgetter('name', 'age')) 
+2

Ti chiedi se 'operator.itemgetter' fa qualcosa di quasi magico? Non è così. Restituisce una tupla (di lunghezza due in questo caso) e quindi 'sorted' fa * The Right Thing * con quello. –

0

Ecco la soluzione generale alternativa: ordina gli elementi di dict con chiavi e valori. Il vantaggio: non è necessario specificare le chiavi e funzionerebbe anche se alcuni tasti mancano in alcuni dizionari.

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) 
Problemi correlati