2012-06-18 32 views
21

Sto lavorando su python 3.2.2. Rompendo la mia testa più di 3 ore per ordinare un dizionario con le sue chiavi. Sono riuscito a renderlo un elenco ordinato con 2 membri argomento, ma non è possibile renderlo un dizionario ordinato alla fine.dizionario di ordinamento python 3

Questo è quello che ho pensato:

myDic={10: 'b', 3:'a', 5:'c'} 
sorted_list=sorted(myDic.items(), key=lambda x: x[0]) 

Ma non importa quello che non posso fare un dizionario da questa lista ordinata. Come lo faccio? Grazie!

+3

Cosa intendi con "non è possibile creare un dizionario da questa lista ordinata"? Un dizionario è non ordinato per definizione. Se vuoi un dizionario ordinato, guarda [Dizionari ordinati] (http://docs.python.org/release/3.1.5/whatsnew/3.1.html#pep-372-ordered-dictionaries). – inspectorG4dget

+0

+1 perché ricordo di aver avuto questo stesso problema (qualche tempo fa prima che OrderedDict fosse ufficialmente inserito in collezioni!) E ricordo la frustrazione ... – jsh

risposta

31

dict non mantiene l'ordine dei suoi elementi. Quello che vi serve è un OrderedDict: http://docs.python.org/library/collections.html#collections.OrderedDict

modificare

Esempio di utilizzo:

>>> from collections import OrderedDict 
>>> a = {'foo': 1, 'bar': 2} 
>>> a 
{'foo': 1, 'bar': 2} 
>>> b = OrderedDict(sorted(a.items())) 
>>> b 
OrderedDict([('bar', 2), ('foo', 1)]) 
>>> b['foo'] 
1 
>>> b['bar'] 
2 
+0

Non riesco a creare un oggetto OrderedDict, digito OrderedDict = {} ma è solo una variabile regolare denominata OrderedDict .. – Jjang

+3

@Jjang, 'OrderedDict = {}' crea solo un dizionario normale denominato "OrderedDict". È necessario eseguire 'from collections import OrderedDict', quindi inizializzare con qualcosa come' myOrdDic = OrderedDict() '. –

+0

@Jjang Ho aggiunto un esempio di utilizzo per chiarezza. –

9

Python di ordinaria dicts non può essere fatto per fornire le chiavi/elementi in un ordine specifico. Per fare ciò, è possibile utilizzare il tipo OrderedDict dal modulo collections. Si noti che il tipo OrderedDict mantiene semplicemente un record di ordine di inserimento. Dovresti ordinare le voci prima di inizializzare il dizionario se vuoi che le visualizzazioni/iteratori successive restituiscano ogni volta gli elementi in ordine. Per esempio:

>>> myDic={10: 'b', 3:'a', 5:'c'} 
>>> sorted_list=sorted(myDic.items(), key=lambda x: x[0]) 
>>> myOrdDic = OrderedDict(sorted_list) 
>>> myOrdDic.items() 
[(3, 'a'), (5, 'c'), (10, 'b')] 
>>> myOrdDic[7] = 'd' 
>>> myOrdDic.items() 
[(3, 'a'), (5, 'c'), (10, 'b'), (7, 'd')] 

Se si vuole mantenere la corretta ordinazione per gli elementi appena aggiunti, si ha realmente bisogno di utilizzare una struttura dati diversa, per esempio, un albero binario/mucchio. Questo approccio di costruire un elenco ordinato e utilizzarlo per inizializzare una nuova istanza OrderedDict() è semplicemente inefficace a meno che i dati non siano completamente statici.

Edit: Quindi, se l'oggetto della selezione dei dati è semplicemente stamparlo in ordine, in un formato simile a un pitone dict oggetto, qualcosa di simile alla seguente dovrebbe essere sufficiente:

def pprint_dict(d): 
    strings = [] 
    for k in sorted(d.iterkeys()): 
     strings.append("%d: '%s'" % (k, d[k])) 
    return '{' + ', '.join(strings) + '}' 

Si noti che questa funzione non è flessibile w/r/t i tipi della chiave, coppie di valori (cioè, si aspetta che le chiavi siano numeri interi e i valori corrispondenti siano stringhe). Se hai bisogno di maggiore flessibilità, usa invece qualcosa come strings.append("%s: %s" % (repr(k), repr(d[k]))).

+0

+1 per "struttura dati diversa". Un 'OrderedDict' è più simile a un dittato/coda ibrido; se non si utilizza una 'coda' per risolvere il problema, un' OrderedDict' probabilmente non è la soluzione giusta. – senderle

+1

@senderle, onestamente, devo ancora incontrare una singola situazione in uno qualsiasi dei miei lavori in cui usare un 'OrderedDict' sarebbe appropriato. Questo non vuol dire che non sia in teoria una classe utile, ma direi che ha un ruolo molto di nicchia. –

+0

ma alla fine rimane una lista, non un dict ... quando si stampa OrderedDict si stampa come una lista, con(), non {} come un dict ... E ho bisogno di stampare in un formato dict nel fine – Jjang

11

Non credo che si desidera un OrderedDict. Sembra che tu preferisca un SortedDict, ovvero un dict che mantiene le sue chiavi in ​​ordine ordinato. Il modulo sortedcontainers fornisce solo un tipo di dati di questo tipo. È scritto in pure-Python, implementazioni veloci come C, ha una copertura del 100% e ore di stress.

L'installazione è facile con pip:

pip install sortedcontainers 

Si noti che se non si può pip install allora si può semplicemente tirare i file di origine dal repository open-source.

Allora sei il codice è semplicemente:

from sortedcontainers import SortedDict 
myDic = SortedDict({10: 'b', 3:'a', 5:'c'}) 
sorted_list = list(myDic.keys()) 

Il modulo sortedcontainers mantiene anche un performance comparison con altre implementazioni popolari.

+2

Per quelli che usano Anaconda, questo è ora disponibile come un'installazione di conda usando 'conda install ordinati contenitori ' – TMWP

0

forse non così buono, ma ho pensato che questo:

def order_dic(dic): 
    ordered_dic={} 
    key_ls=sorted(dic.keys()) 
    for key in key_ls: 
     ordered_dic[key]=dic[key] 
    return ordered_dic 
+1

Hai provato il tuo codice? Dubito che funzioni affatto. Il tuo 'ordered_dic' è ugualmente non ordinato come' dic'. Quando si itera un 'dict standard, gli elementi non vengono restituiti nell'ordine in cui sono stati inseriti. Per questo hai bisogno di "OrderedDict". –

0

Qualsiasi soluzione moderna a questo problema? ho lavorato intorno ad esso con:

order = sorted([ job['priority'] for job in self.joblist ]) 
    sorted_joblist = [] 
    while order: 
     min_priority = min(order) 
     for job in self.joblist: 
      if job['priority'] == min_priority: 
       sorted_joblist += [ job ] 
       order.remove(min_priority) 
    self.joblist = sorted_joblist 

La lista di job è formattato come: lista di job = [{ 'priorità': 3, 'name': 'foo', ...}, { 'priorità': 1 , 'name': 'bar', ...}]

  • Fondamentalmente ho creare un elenco (ordine) con tutti gli elementi con cui voglio ordinare la dict
  • poi ho iterare questo elenco e la Detto, quando trovo l'oggetto sul ditt, lo mando a un nuovo dict e rimuovo l'oggetto da 'ordine'.

Sembra funzionare, ma suppongo che ci siano soluzioni migliori.

0

Una soluzione moderna e veloce, per Python 3.7. Può funzionare anche su alcuni interpreti di Python 3.6.

TLDR

Per ordinare un dizionario da usare i tasti:

sorted_dict = {k: disordered[k] for k in sorted(disordered)} 

quasi tre volte più veloce la risposta accettata; probabilmente di più quando includi le importazioni.

Commenta la risposta accettata

L'esempio nella risposta accettata non utilizza il parametro di key per sorted() che (concesso, sembra piacevole e richiede meno battitura) è più lento sia come elementi di tuple generate (key, value) sono rispetto. Poiché i dizionari Python non possono avere due elementi con la stessa chiave, non è necessario utilizzare il secondo elemento della tupla per il confronto.

Come Ordina per chiave in Python 3,7

Il grande cambiamento in Python 3.7 è che the dictionaries are now ordered by default.

  • È possibile generare dict ordinato utilizzando le comprensioni del testo.
  • L'utilizzo di OrderedDict potrebbe comunque essere preferibile per motivi di compatibilità.
  • Non utilizzare sorted(d.items()) senza key.

See:

disordered = {10: 'b', 3: 'a', 5: 'c'} 

# sort keys, then get values from original - fast 
sorted_dict = {k: disordered[k] for k in sorted(disordered)} 

# key = itemgetter - slower 
from operator import itemgetter 
key = itemgetter(0) 
sorted_dict = {k: v for k, v in sorted(disordered.items(), key=key)} 

# use key = lambda - the slowest 
key = lambda item: item[0] 
sorted_dict = {k: v for k in sorted(disordered.items(), key=key)} 

Generalmente

Timing risultati:

Best for {k: d[k] for k in sorted(d)}: 7.507327548999456 
Best for {k: v for k, v in sorted(d.items(), key=key_getter)}: 12.031082626002899 
Best for {k: v for k, v in sorted(d.items(), key=key_lambda)}: 14.22885995300021 

Best for dict(sorted(d.items(), key=key_getter)): 11.209122000000207 
Best for dict(sorted(d.items(), key=key_lambda)): 13.289728325995384 
Best for dict(sorted(d.items())): 14.231471302999125 

Best for OrderedDict(sorted(d.items(), key=key_getter)): 16.609151654003654 
Best for OrderedDict(sorted(d.items(), key=key_lambda)): 18.52622927199991 
Best for OrderedDict(sorted(d.items())): 19.436101284998585 

Testing codice:

from timeit import repeat 

setup_code = """ 
from operator import itemgetter 
from collections import OrderedDict 
import random 
random.seed(0) 
d = {i: chr(i) for i in [random.randint(0, 120) for repeat in range(120)]} 
key_getter = itemgetter(0) 
key_lambda = lambda item: item[0] 
""" 

cases = [ 
    # fast 
    '{k: d[k] for k in sorted(d)}', 
    '{k: v for k, v in sorted(d.items(), key=key_getter)}', 
    '{k: v for k, v in sorted(d.items(), key=key_lambda)}', 
    # slower 
    'dict(sorted(d.items(), key=key_getter))', 
    'dict(sorted(d.items(), key=key_lambda))', 
    'dict(sorted(d.items()))', 
    # the slowest 
    'OrderedDict(sorted(d.items(), key=key_getter))', 
    'OrderedDict(sorted(d.items(), key=key_lambda))', 
    'OrderedDict(sorted(d.items()))', 
] 

for code in cases: 
    times = repeat(code, setup=setup_code, repeat=3) 
    print(f"Best for {code}: {min(times)}")