2009-10-07 1 views
10

Ho dict in pitone con chiavi della seguente forma:Posso ordinare il testo in base al suo valore numerico in Python?

mydict = {'0'  : 10, 
      '1'  : 23, 
      '2.0' : 321, 
      '2.1' : 3231, 
      '3'  : 3, 
      '4.0.0' : 1, 
      '4.0.1' : 10, 
      '5'  : 11, 
      # ... etc 
      '10' : 32, 
      '11.0' : 3, 
      '11.1' : 243, 
      '12.0' : 3, 
      '12.1.0': 1, 
      '12.1.1': 2, 
      } 

Alcuni degli indici hanno sotto-valori, alcuni hanno un livello di sotto-valori e alcuni hanno due. Se avessi solo un sottolivello potrei trattarli tutti come numeri e ordinare numericamente. Il secondo sottolivello mi costringe a gestirli tutti come stringhe. Tuttavia, se li ordino come stringhe, avrò 10 dopo 1 e 20 dopo 2.

Come posso ordinare gli indici correttamente?

Nota: Quello che voglio veramente è stampare il dict ordinato per indice. Se c'è un modo migliore di farlo, in qualche modo va bene per me.

+2

Questo è chiamato "ordinamento naturale". Vedi http://stackoverflow.com/questions/34518/natural-sorting-algorithm –

risposta

18

È possibile ordinare i tasti come desiderato, dividendoli in "." e poi la conversione di ciascuno dei componenti in un intero, come questo:

sorted(mydict.keys(), key=lambda a:map(int,a.split('.'))) 

che restituisce questo:

['0', 
'1', 
'2.0', 
'2.1', 
'3', 
'4.0.0', 
'4.0.1', 
'5', 
'10', 
'11.0', 
'11.1', 
'12.0', 
'12.1.0', 
'12.1.1'] 

È possibile iterare su quella lista di chiavi, ed estrarre i valori dal vostro dizionario come necessario.

Si potrebbe anche ordinare il risultato di mydict.items(), molto simile:

sorted(mydict.items(), key=lambda a:map(int,a[0].split('.'))) 

Questo vi dà un elenco ordinato di (chiave, valore) coppie, in questo modo:

[('0', 10), 
('1', 23), 
('2.0', 321), 
('2.1', 3231), 
('3', 3), 
# ... 
('12.1.1', 2)] 
+0

E ovviamente non è necessario utilizzare un lambda, è possibile definire una funzione nel modo consueto e inserire il nome. – steveha

+0

Più veloce della versione del comparatore (meno conversioni/chiamate di funzione). – bobince

2

funzioni di ordinamento di Python può prendere un costume confrontare la funzione, in modo che solo bisogno di definire una funzione che confronta le chiavi nel modo che piace:

def version_cmp(a, b): 
    '''These keys just look like version numbers to me....''' 
    ai = map(int, a.split('.')) 
    bi = map(int, b.split('.')) 
    return cmp(ai, bi) 

for k in sorted(mydict.keys(), version_cmp): 
    print k, mydict[k] 

In questo caso, è preferibile utilizzare il parametro key su sorted(). Vedere la risposta Ian Clelland per un esempio.

+1

È meglio fornire la funzione chiave come fa Ian. La funzione chiave viene chiamata una volta per ogni elemento, la funzione cmp viene chiamata ogni volta che l'ordinamento esegue un confronto. –

+0

@gnibbler: Sì, mi sono reso conto che dopo aver letto la risposta di Ian ... stavo pensando di cancellare la mia risposta, ma immagino che potrebbe essere utile per qualcuno che ha bisogno di un confronto più complicato che non può essere fatto passando un semplice ' key'. – sth

2

Come addendum alla risposta Ian Clelland's, la chiamata map() può essere sostituita con una lista di comprensione ... se si preferisce quello stile. È può essere anche più efficiente (anche se negligentemente in questo caso, sospetto).

sorted(mydict.keys(), key=lambda a: [int(i) for i in a.split('.')])

1

Per divertimento & utilità (per ppl googling, per lo più):

f = lambda i: [int(j) if re.match(r"[0-9]+", j) else j for j in re.findall(r"([0-9]+|[^0-9]+)", i)] 
cmpg = lambda x, y: cmp(f(x), f(y)) 

uso come sorted(list, cmp=cmpg). Inoltre, le regex potrebbero essere precompilate (raramente è necessario, in realtà, con la cache del modulo re). E, può essere (facilmente) modificato, ad esempio, per includere valori negativi (aggiungere -? a num regex, probabilmente) e/o per utilizzare valori float.

Potrebbe non essere molto efficiente, ma anche con quello è abbastanza utile.

E, uhm, può essere utilizzato come chiave = anche per ordinato().