2011-01-14 34 views
18

A question was asked here on SO, pochi minuti fa, sull'ordinamento delle chiavi del dizionario in base ai loro valori.Ordinamento del dizionario utilizzando operator.itemgetter

Ho appena letto sul metodo operator.itemgetter di ordinare alcuni giorni indietro e ho deciso di provarlo, ma non sembra funzionare.

Non che io abbia problemi con le risposte presentate alle domande, volevo solo provare con operator.itemgetter.

Così il dict era:

>>> mydict = { 'a1': ['g',6], 
      'a2': ['e',2], 
      'a3': ['h',3], 
      'a4': ['s',2], 
      'a5': ['j',9], 
      'a6': ['y',7] } 

ho provato questo:

>>> l = sorted(mydict.itervalues(), key=operator.itemgetter(1)) 
>>> l 
[['e', 2], ['s', 2], ['h', 3], ['g', 6], ['y', 7], ['j', 9]] 

E questo funziona come voglio io. Tuttavia, dato che non ho il dizionario completo (mydict.itervalues()), ho provato questo:

>>> complete = sorted(mydict.iteritems(), key=operator.itemgetter(2)) 

Questo non funziona (come mi aspettavo che).

Quindi, come si ordina il dict utilizzando operator.itemgetter e si chiama itemgetter sulla coppia di chiavi nidificate - valore.

risposta

31
In [6]: sorted(mydict.iteritems(), key=lambda (k,v): operator.itemgetter(1)(v)) 
Out[6]: 
[('a2', ['e', 2]), 
('a4', ['s', 2]), 
('a3', ['h', 3]), 
('a1', ['g', 6]), 
('a6', ['y', 7]), 
('a5', ['j', 9])] 

Il parametro chiave è sempre una funzione che viene alimentata una voce dall'iter iterabile (mydict.iteritems()) alla volta. In questo caso, un elemento potrebbe essere qualcosa di simile

('a2',['e',2]) 

quindi abbiamo bisogno di una funzione che può prendere ('a2',['e',2]) come input e restituire 2.

lambda (k,v): ... è una funzione anonima che prende un argomento - un 2- tupla - e la decomprime in k e v. Pertanto, quando la funzione lambda viene applicata al nostro articolo, sarebbe 'a2' e v sarebbe ['e',2].

lambda (k,v): operator.itemgetter(1)(v) applicata al nostro articolo torna così operator.itemgetter(1)(['e',2]), che "itemgets" la seconda voce in ['e',2], che è 2.

Nota che lambda (k,v): operator.itemgetter(1)(v) non è un buon modo per codificare in Python.Come sottolinea gnibbler, lo operator.itemgetter(1) viene ricalcolato per ogni articolo. Questo è inefficiente. Il punto di utilizzo di operator.itemgetter(1) consiste nel creare una funzione che può essere applicata più volte. Non si desidera ricreare la funzione ogni volta. lambda (k,v): v[1] è più leggibile e più veloce:

In [15]: %timeit sorted(mydict.iteritems(), key=lambda (k,v): v[1]) 
100000 loops, best of 3: 7.55 us per loop 

In [16]: %timeit sorted(mydict.iteritems(), key=lambda (k,v): operator.itemgetter(1)(v)) 
100000 loops, best of 3: 11.2 us per loop 
+0

Potete spiegare questo: 'operator.itemgetter (1) (v))'? – user225312

+3

@ A: 'operator.itemgetter (1) (v)' è equivalente a 'v [1]'. Puoi sostituire qualsiasi occorrenza di 'a [b]' con 'operator.itemgetter (b) (a)', ma questo non è il modo in cui * I * ha capito la tua domanda :) –

+4

Creare un itemgetter è relativamente costoso. Questo crea uno per ogni chiave nel dict –

5

La risposta è - non è possibile. operator.itemgetter(i) restituisce un richiamabile che restituisce l'elemento i della sua tesi, cioè

f = operator.itemgetter(i) 
f(d) == d[i] 

non potrà mai tornare simething come d[i][j]. Se davvero si vuole fare questo in uno stile puramente funzionale, è possibile scrivere il proprio compose() funzione:

def compose(f, g): 
    return lambda *args: f(g(*args)) 

e utilizzare

sorted(mydict.iteritems(), key=compose(operator.itemgetter(1), 
             operator.itemgetter(1))) 

Nota che non mi raccomando di farlo :)

+0

'unutbu' ha una soluzione, anche se non è chiara. Lo aspetteremo per aggiornarlo. – user225312

5

itemgetter non supporta nidificazione (anche se attrgetter fa)

avresti bisogno di appiattire la dict come questo

sorted(([k]+v for k,v in mydict.iteritems()), key=itemgetter(2)) 
+0

+1, abbastanza buono! – user225312

Problemi correlati