2010-03-30 9 views

risposta

10

Come chiunque altro ha sottolineato, dizionari hanno un proprio ordinamento e non si può semplicemente ordinarli come si farebbe un elenco.

Una cosa che vorrei aggiungere è che, se si desidera solo per passare attraverso gli elementi di un dizionario in ordine sequenziale, questo è solo:

for k in sorted(a): 
    print k, a[k] # or whatever. 

Se si preferisce avere una lista di comprensione (per Alex):

sortedlist = [(k, a[k]) for k in sorted(a)] 

vorrei far notare che l'uso di Alex di key=int non funziona con il vostro esempio perché una delle chiavi è 'test'. Se si vuole veramente che i numeri ordinati prima che i non-numerici, si dovrà passare in una funzione cmp:

def _compare_keys(x, y): 
    try: 
     x = int(x) 
    except ValueError: 
     xint = False 
    else: 
     xint = True 
    try: 
     y = int(y) 
    except ValueError: 
     if xint: 
      return -1 
     return cmp(x.lower(), y.lower()) 
     # or cmp(x, y) if you want case sensitivity. 
    else: 
     if xint: 
      return cmp(x, y) 
     return 1 

for k in sorted(a, cmp=_compare_keys): 
    print k, a[k] # or whatever. 

O forse si sa abbastanza circa le chiavi per scrivere una funzione per convertirli in una stringa (o un altro oggetto) che ordina a destra:

# Won't work for integers with more than this many digits, or negative integers. 
MAX_DIGITS = 10 
def _keyify(x): 
    try: 
     xi = int(x) 
    except ValueError: 
     return 'S{0}'.format(x) 
    else: 
     return 'I{0:0{1}}'.format(xi, MAX_DIGITS) 

for k in sorted(a, key=_keyify): 
    print k, a[k] # or whatever. 

Questo sarebbe molto più veloce rispetto all'utilizzo di una funzione cmp.

+0

Non mi piace tanto quanto le altre soluzioni. '_keyify' è inutilmente rigido e non penso sia così pulito come alcune delle altre soluzioni e il tuo' _compare_keys' usa 'cmp', che di solito è un brutto segno. (È anche una buona forma tenere i blocchi 'try' per' try'/'except' il più piccolo possibile.Porrei mettere' return 'I {0: 0 {1}}.. Format (xi, MAX_DIGITS) 'in un 'else' block.) –

+0

No, le soluzioni non sono ottimizzate, sono solo lì come esempi o punti di partenza. (È difficile suggerire una soluzione migliore senza sapere quale sia il vero dominio della chiave.) Mi piace la tua soluzione eccetto per la parte in cui si rompe in Python 3. Originariamente ho scritto '_compare_keys' con due blocchi' try' a due righe, ma era il doppio del tempo. L'utilizzo dei blocchi 'try' per controllare il flusso di esecuzione ha eliminato la necessità di un booleano' yint'. –

+0

Suppongo che tu intenda che usare il parametro 'cmp', al contrario della funzione, sia un" cattivo segno ", e sì lo è, ma è causato dal dominio della chiave completamente definito. Ecco perché ho seguito la soluzione '_keyify'. Sarebbe anche meglio creare una nuova classe per le chiavi che possano prendere un input di stringa e ordinarlo correttamente, e usarlo come chiave del dizionario, ma non so se ben ha il controllo sul dizionario di input. E, sì, mi sono dimenticato dei blocchi 'else'; troppo C++ di recente. Fisso. –

4

I dizionari non sono ordinati. Non è possibile ordinare uno come si mostra perché il risultante a è un dict, e le diciture non hanno ordine.

Se si desidera, ad esempio, una lista un elenco delle chiavi in ​​modo ordinato, è possibile utilizzare il codice come

>>> def my_key(dict_key): 
...  try: 
...   return int(dict_key) 
...  except ValueError: 
...   return dict_key 
... 
>>> sorted(a, key=my_key) 
['1', '6', '67', '88', '100', 'test'] 

Questo si basa sul comportamento Python stupido che i casi di str sono sempre maggiore di casi di int. (Il comportamento è stato risolto in Python 3.) In un progetto ottimale, le chiavi del tuo dict sarebbero cose che si possono confrontare in modo sano e non si sarebbe mescolare in stringhe che rappresentano numeri con le stringhe che rappresentano parole.

Se si desidera mantenere le chiavi in ​​ordine sempre ordinato, è possibile utilizzare il modulo bisect o implementare una mappatura che si basa su una struttura di dati dell'albero. Il modulo bisect non accetta un argomento key come la roba di ordinamento, perché questo sarebbe potenzialmente inefficiente; si può usare il modello di decorare-uso-undecorate se si è scelto di utilizzare bisect, mantenendo un elenco ordinato che dipende dal risultato della funzione chiave.

+0

Si noti che il mantenimento di un elenco ordinato utilizzando la bisettrice richiede il tempo O (n ** 2). Il bisect troverà il posto giusto da inserire in O (log n) tempo per elemento, ma l'inserimento richiede ancora O (n) tempo per elemento a causa della natura simile a un array degli elenchi Python. –

+0

@Daniel Strutzbach, Dipende da cosa intendi con "mantenimento"; a seconda dell'utilizzo, è possibile evitare il comportamento quadratico della creazione di una lista ordinata utilizzando l'ortografia. L'utilizzo di un elenco e di "bisect" non offre certamente le prestazioni ideali per le operazioni di inserimento. Se queste operazioni sono importanti, è meglio andare con "o implementare una mappatura che si basa su una struttura di dati dell'albero". Dato che l'hai già fatto per noi, ho rialzato il tuo post che fa riferimento alla tua bella implementazione 'sorteddict'. –

6

Non è possibile ordinare uno dict in Python poiché il tipo dict è intrinsecamente non ordinato. Quello che puoi fare è ordinare gli oggetti prima di usarli usando la funzione integrata sorted(). Avrete anche bisogno di una funzione di supporto per distinguere tra le numerici e stringhe tasti:

def get_key(key): 
    try: 
     return int(key) 
    except ValueError: 
     return key 
a = {'100':12,'6':5,'88':3,'test':34, '67':7,'1':64 } 
print sorted(a.items(), key=lambda t: get_key(t[0])) 

Tuttavia in Python 3.1 (e 2.7) il modulo collections contiene il tipo collections.OrderedDict che può essere utilizzato per ottenere l'effetto desiderato come qui di seguito :

def get_key(key): 
    try: 
     return int(key) 
    except ValueError: 
     return key 
a = {'100':12,'6':5,'88':3,'test':34, '67':7,'1':64 } 
b = collections.OrderedDict(sorted(a.items(), key=lambda t: get_key(t[0]))) 
print(b) 
+0

Questo non otterrà l'ordine OP voluto. –

+0

Ciao Mawushe, Innanzitutto vorrei ringraziarvi per il vostro suggerimento. Ma ancora il codice indicato non fornirà una soluzione desiderata. – Joseph

+0

Siete entrambi degli uomini giusti Ho aggiornato la risposta ad entrambi i tipi dalla chiave e tiene conto della natura delle stringhe delle chiavi. –

5

9 anni fa ho postato un recipe che inizia

I dizionari non possono essere ordinati: una mappatura non ha ordini!

e mostra come ottenere allineati liste di chiavi e valori di un dict.

Con Python di oggi, e le caratteristiche del tuo-plus-implicita espressi, io suggerirei:

import sys 

def asint(s): 
    try: return int(s), '' 
    except ValueError: return sys.maxint, s 

sortedlist = [(k, a[k]) for k in sorted(a, key=asint)] 

il key=asint è quello che dice sorted per il trattamento di quelle chiavi stringa come numeri interi a fini di smistamento, in modo che per esempio '2' sorta tra '1' e '12', piuttosto che dopo di loro sia - questo è quello che sembra richiedere, oltre ad avere tutte le chiavi non-all-cifre in ordine tutto quelle all-cifre. Se devi anche gestire stringhe di chiavi a tutte le cifre che esprimono numeri interi maggiori di sys.maxint, è un po 'più complicato, ma ancora fattibile:

class Infinity(object): 
    def __cmp__(self, other): return 0 if self is other else 1 
infinite = Infinity() 
def asint(s): 
    try: return int(s), '' 
    except ValueError: return infinite, s 

In generale, è possibile ottenere risposte migliori più velocemente se si specifica vostre esigenze più precisamente dall'inizio ;-).

+0

Questo fallirà per l'OP di input specificato perché 'int' genererà' ValueError' quando incontra ''test''. –

+0

@Mike, giusto - lascia che lo aggiusti. –

+0

Ciao Alex, Grazie per il vostro gentile aiuto e consiglio. Ha funzionato...! – Joseph

2

Se si installa il pacchetto blist, include un tipo sorteddict. Poi si può semplicemente:

from blist import sorteddict 

def my_key(dict_key): 
     try: 
       return int(dict_key) 
     except ValueError: 
       return dict_key 

a = {'100':12,'6':5,'88':3,'test':34, '67':7,'1':64 } 
print sorteddict(my_key, **a).keys() 

uscita:

['1', '6', '67', '88', '100', 'test'] 
+0

Rientri di 7 spazi a parte ';)', questa è una buona soluzione se hai bisogno di una mappatura sempre ordinata. –

Problemi correlati