En Python, ¿cómo itero sobre un diccionario en orden de clave ordenado?

211

Hay una función existente que termina en lo siguiente, donde dhay un diccionario:

return d.iteritems()

que devuelve un iterador sin clasificar para un diccionario dado. Me gustaría devolver un iterador que recorra los elementos ordenados por clave . ¿Cómo puedo hacer eso?

Miguel
fuente

Respuestas:

171

No lo he probado exhaustivamente, pero funciona en Python 2.5.2.

>>> d = {"x":2, "h":15, "a":2222}
>>> it = iter(sorted(d.iteritems()))
>>> it.next()
('a', 2222)
>>> it.next()
('h', 15)
>>> it.next()
('x', 2)
>>>

Si está acostumbrado a hacer en for key, value in d.iteritems(): ...lugar de iteradores, esto seguirá funcionando con la solución anterior

>>> d = {"x":2, "h":15, "a":2222}
>>> for key, value in sorted(d.iteritems()):
>>>     print(key, value)
('a', 2222)
('h', 15)
('x', 2)
>>>

Con Python 3.x, use en d.items()lugar de d.iteritems()devolver un iterador.

jpp
fuente
29
use en .items()lugar de iteritems(): como dijo @Claudiu, iteritems no funciona para Python 3.x, pero items()está disponible desde Python 2.6.
Remi
40
Esto no es obvio. De hecho, items()crea una lista y, por lo tanto, usa memoria, mientras que iteritems()esencialmente no usa memoria. Lo que debe usar depende principalmente del tamaño del diccionario. Además, la herramienta de conversión automática de Python 2 a Python 3 ( 2to3) se encarga automáticamente de la conversión de iteritems()a items(), por lo que no hay necesidad de preocuparse por esto.
Eric O Lebigot
55
@HowerHell utiliza y collections.OrderedDictluego ordena una vez y obtiene los elementos en orden ordenado siempre.
Mark Harviston
99
Pero @EOL, incluso si iteritems()no usa memoria, todo se debe extraer de la memoria sorted(), por lo que no hay diferencia entre el uso de la memoria items()y iteritems()aquí.
Richard
8
@ Richard: Si bien es cierto que todos los elementos deben extraerse en la memoria, se almacenan dos veces con items()(en la lista devuelta por items()y en la lista ordenada) y solo una vez con iteritems()(solo en la lista ordenada).
Eric O Lebigot
83

Usa la sorted()función:

return sorted(dict.iteritems())

Si desea un iterador real sobre los resultados ordenados, ya que sorted()devuelve una lista, use:

return iter(sorted(dict.iteritems()))
Greg Hewgill
fuente
Eso me falla: <type 'exceptions.TypeError'>: iter () devolvió el no iterador del tipo 'list'
mike
Probablemente sea porque usas "dict" como el nombre de la variable. "dict" es en realidad el nombre de tipo de los diccionarios. Simplemente use otro nombre como "mydict" aquí y listo.
utku_karatas
1
Sigue sin funcionar. ¿Eres positivo sorted () devuelve otro iterador, en lugar de una lista normal?
Mike
cuando y donde ocurre esta excepción? puedes recorrer una lista sin problemas
1
De acuerdo, hop. No creo que llame a .next () directamente, excepto cuando se omiten líneas en los archivos. Nuestra solución iter (sorted (dict.iteritems ())) termina haciendo una copia de todo el dict en la memoria en la etapa "sorted (" de todos modos, por lo que el beneficio del iterador principal parece perdido :)
39

Las claves de un dict se almacenan en una tabla hash, por lo que es su "orden natural", es decir, psuedo-random. Cualquier otro pedido es un concepto del consumidor del dict.

sorted () siempre devuelve una lista, no un dict. Si le pasa un dict.items () (que produce una lista de tuplas), devolverá una lista de tuplas [(k1, v1), (k2, v2), ...] que se pueden usar en un bucle de una manera muy parecida a un dict, pero de todos modos no es un dict !

foo = {
    'a':    1,
    'b':    2,
    'c':    3,
    }

print foo
>>> {'a': 1, 'c': 3, 'b': 2}

print foo.items()
>>> [('a', 1), ('c', 3), ('b', 2)]

print sorted(foo.items())
>>> [('a', 1), ('b', 2), ('c', 3)]

Lo siguiente se siente como un dict en un bucle, pero no lo es, es una lista de tuplas que se descomprimen en k, v:

for k,v in sorted(foo.items()):
    print k, v

Aproximadamente equivalente a:

for k in sorted(foo.keys()):
    print k, foo[k]
Peter Rowell
fuente
De acuerdo, pero no quiero un Dict o una Lista, quiero un Iterator. ¿Cómo lo obligo a ser un iterador?
Mike
2
sorted(foo.keys())es mejor como equivalente sorted(foo), ya que los diccionarios devuelven sus claves cuando se repiten (con la ventaja de no verse obligados a crear la foo.keys()lista intermedia, tal vez, dependiendo de cómo sorted()se implemente para los iterables).
Eric O Lebigot
Es de extrañar que es mejor para la velocidad y / o la memoria k in sorted(foo.keys()):que tira las llaves o for k,v in sorted(foo.items()):que devuelve una copia de la lista de pares del diccionario Conjeturaríasorted(foo.keys())
CrandellWS
1
@CrandellWS: La mejor manera de responder la pregunta de tiempo es con el módulo de tiempo de Python .
Peter Rowell
1
@frank - Respuesta breve: No. Un dict es una matriz con la clave real como un hash del valor de la clave suministrada. Aunque algunas implementaciones pueden ser bastante predecibles, y algunas incluso pueden hacer este contrato, no cuento con nada cuando se trata de ordenar hash. Vea esta publicación para más información sobre el comportamiento 3.6+. En particular, tenga en cuenta la primera respuesta.
Peter Rowell
31

La respuesta de Greg es correcta. Tenga en cuenta que en Python 3.0 tendrá que hacer

sorted(dict.items())

como iteritemsse habrá ido

Claudiu
fuente
Eso falla para mí: <type 'exceptions.TypeError'>: iter () devolvió el no iterador de tipo 'list'
mike
3
"No utilices autos porque en el futuro tendremos hoverboards"
JJ
7

Ahora puede usar también OrderedDicten Python 2.7:

>>> from collections import OrderedDict
>>> d = OrderedDict([('first', 1),
...                  ('second', 2),
...                  ('third', 3)])
>>> d.items()
[('first', 1), ('second', 2), ('third', 3)]

Aquí tienes la página de novedades para la versión 2.7 y la API OrderedDict .

Caumons
fuente
Eso devolverá la clave, los valores en el orden en que se insertan, no en un orden ordenado (es decir, alfabético).
Tony Suffolk 66
5

En general, uno puede ordenar un dict así:

for k in sorted(d):
    print k, d[k]

Para el caso específico en la pregunta, que tiene una "caída de reemplazo" para d.iteritems (), agregue una función como:

def sortdict(d, **opts):
    # **opts so any currently supported sorted() options can be passed
    for k in sorted(d, **opts):
        yield k, d[k]

y entonces la línea final cambia de

return dict.iteritems()

a

return sortdict(dict)

o

return sortdict(dict, reverse = True)
pitonaria
fuente
5
>>> import heapq
>>> d = {"c": 2, "b": 9, "a": 4, "d": 8}
>>> def iter_sorted(d):
        keys = list(d)
        heapq.heapify(keys) # Transforms to heap in O(N) time
        while keys:
            k = heapq.heappop(keys) # takes O(log n) time
            yield (k, d[k])


>>> i = iter_sorted(d)
>>> for x in i:
        print x


('a', 4)
('b', 9)
('c', 2)
('d', 8)

Este método todavía tiene una clasificación O (N log N), sin embargo, después de un breve heapify lineal, produce los elementos en orden ordenado a medida que avanza, haciéndolo teóricamente más eficiente cuando no siempre necesita la lista completa.

jamylak
fuente
4

Si desea ordenar por el orden en que se insertaron los elementos en lugar del orden de las teclas, debe echar un vistazo a las colecciones de Python . (Solo Python 3)

gecco
fuente
3

sorted devuelve una lista, de ahí su error cuando intenta iterar sobre ella, pero debido a que no puede ordenar un dict tendrá que lidiar con una lista.

No tengo idea de cuál es el contexto más amplio de su código, pero podría intentar agregar un iterador a la lista resultante. ¿Tal vez así?

return iter(sorted(dict.iteritems()))

por supuesto, volverás a recibir tuplas ahora porque ordenado convirtió tu dict en una lista de tuplas

ex: digamos que su dict fue: {'a':1,'c':3,'b':2} ordenado lo convierte en una lista:

[('a',1),('b',2),('c',3)]

así que cuando iteras sobre la lista obtienes (en este ejemplo) una tupla compuesta de una cadena y un entero, pero al menos podrás iterar sobre ella.

pcn
fuente
2

Suponiendo que está utilizando CPython 2.xy un mydict de diccionario grande, usar sorted (mydict) será lento porque sorted crea una lista ordenada de las claves de mydict.

En ese caso, es posible que desee consultar el paquete de mi pedido ordenado que incluye una implementación de sorteddictC en C. Especialmente si tiene que revisar la lista ordenada de claves varias veces en diferentes etapas (es decir, número de elementos) de la vida útil de los diccionarios.

http://anthon.home.xs4all.nl/Python/ordereddict/

Anthon
fuente