2010-03-30 7 views

Respuesta

10

Como todo el mundo ha señalado, los diccionarios tienen su propio ordenamiento y que no sólo puede ordenarlos como lo haría una lista.

Una cosa que me gustaría añadir es que, si lo que desea es ir a través de los elementos de un diccionario en forma ordenada, eso es sólo:

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

Si prefieres tener una lista por comprensión (por Alex):

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

me gustaría señalar que el uso de Alex de key=int no funcionará con su ejemplo, porque una de sus claves es 'test'. Si realmente desea que los números ordenados antes que los no-numéricos, que tendrá que pasar en una función 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 tal vez usted sabe lo suficiente acerca de sus claves para escribir una función para convertirlos en una cadena (u otro objeto) que ordena la derecha:

# 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. 

Esto sería mucho más rápido que usar una función cmp.

+0

Realmente no me gusta tanto como las otras soluciones. '_keyify' es innecesariamente rígido y no creo que sea tan limpio como algunas de las otras soluciones y tu' _compare_keys' usa 'cmp', que generalmente es una mala señal. (También es una buena forma mantener los bloques 'try' para' try'/'except' lo más pequeño posible. Pondría' return' I {0: 0 {1}} '. Format (xi, MAX_DIGITS) 'en un 'else' block.) –

+0

No, las soluciones no están optimizadas, solo están ahí como ejemplos o puntos de partida. (Es difícil sugerir una mejor solución sin saber cuál es el dominio clave real). Me gusta su solución, excepto la parte donde se rompe en Python 3. Originalmente escribí '_compare_keys' con dos bloques' try' de dos líneas, pero fue dos veces más largo. El uso de los bloques 'try' para controlar el flujo de ejecución eliminó la necesidad de un booleano' yint'. –

+0

Supongo que quiere decir que usar el parámetro 'cmp', a diferencia de la función, es un" signo incorrecto ", y sí lo es, pero es provocado por el dominio clave incompletamente definido. Es por eso que seguí con la solución '_keyify'. Aún mejor sería crear una nueva clase para claves que pueda tomar una entrada de cadena y ordenarla correctamente, y usarla como la clave del diccionario, pero no sé si ben tiene tanto control sobre el diccionario de entrada. Y, sí, me olvidé de los bloques 'else'; demasiado C++ recientemente. Fijo. –

4

Los diccionarios no están ordenados. No puede ordenar uno como se muestra porque el resultante a es un dict y los dicts no tienen orden.

Si desea, por ejemplo, una lista una lista de las claves en el orden establecido, se puede utilizar un código como

>>> 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'] 

Esto se basa en el comportamiento de Python estúpida que los casos de str son siempre mayores que las instancias de int. (El comportamiento se soluciona en Python 3.) En un diseño óptimo, las claves de su dict serían cosas que podría comparar sanamente y no se mezclarían en cadenas que representan números con cadenas que representan palabras.

Si desea mantener las claves en orden ordenado siempre, puede utilizar el módulo bisect o implementar una asignación que se base en una estructura de datos de árbol. El módulo bisect no acepta un argumento key como los elementos de clasificación, ya que podría ser potencialmente ineficiente; usted usaría el patrón decorate-use-undecorate si elige usar bisect, manteniendo una lista ordenada que depende del resultado de la función de la tecla.

+0

Tenga en cuenta que mantener una lista ordenada mediante bisección lleva el tiempo O (n ** 2). La bisección encontrará el lugar correcto para insertar en O (log n) el tiempo por artículo, pero la inserción todavía toma O (n) tiempo por artículo debido a la naturaleza similar a una matriz de las listas de Python. –

+0

@Daniel Strutzbach, eso depende de lo que quieras decir con "mantener"; Dependiendo del uso, a menudo puede evitar el comportamiento cuadrático de construir una lista ordenada utilizando insort. El uso de una lista y 'bisect' ciertamente no ofrece el rendimiento ideal para las operaciones de inserción. Si estas operaciones son importantes, es mejor ir con "o implementar un mapeo que se base en una estructura de datos de árbol". Como ya ha hecho esto por nosotros, subí su publicación que hace referencia a su agradable implementación de 'sorteddict'. –

6

No se puede ordenar un dict en Python ya que el tipo dict está inherentemente desordenado. Lo que puede hacer es ordenar los elementos antes de usarlos usando la función integrada sorted(). También necesitará una función de ayuda para distinguir entre sus numéricas y de cadena claves:

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])) 

Sin embargo en Python 3.1 (y 2.7) del módulo collections contiene el tipo collections.OrderedDict que se puede utilizar para lograr el efecto deseado, como a continuación :

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

Esto no obtendrá la orden que OP quería. –

+0

Hola Mawushe, Primero déjenme agradecerles su sugerencia. Pero aún el código dado no proporcionará la solución deseada. – Joseph

+0

Ambos son expertos. He actualizado la respuesta para ordenar por la clave y tener en cuenta la naturaleza de la cadena de las teclas. –

5
hace

9 años he publicado un recipe que comienza

Los diccionarios no se pueden ordenar: ¡una asignación no tiene orden!

y muestra cómo obtener ordenadas listas de claves y valores de un diccionario.

Con Python de hoy, y sus especificaciones-plus-implícita expresadas, me gustaría sugerir:

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)] 

la key=asint es lo que dice sorted para tratar esas claves de cadena como enteros con fines de clasificación, de modo que, por ejemplo, '2' ordena entre '1' y '12', en lugar de después de ambos: eso es lo que parece necesitar, así como tener todas las teclas que no son de todos los dígitos, después de todo de dígitos enteros. Si necesita tratar también cadenas de clave de todos los dígitos que expresen números enteros mayores que sys.MAXINT, que es un poco más complicado, pero se puede hacer:

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 

En general, se pueden obtener mejores respuestas más rápidamente si especifica sus requisitos exactos, más precisamente desde el principio ;-).

+0

Esto fallará para la entrada OP especificada porque 'int' levantará' ValueError' cuando encuentre ''test''. –

+0

@Mike, a la derecha, déjame arreglar eso. –

+0

Hola Alex, Gracias por su amable ayuda y consejo. Funcionó...! – Joseph

2

Si instala mi paquete blist, incluye un tipo sorteddict. Posteriormente, se podría simplemente:

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() 

Salida:

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

sangrías de 7 espacios aparte ';)', esta es una buena solución si necesita una asignación que siempre esté ordenada. –

Cuestiones relacionadas