En Python, comment parcourir un dictionnaire dans un ordre de clé trié?

211

Il existe une fonction existante qui se termine par ce qui suit, où se dtrouve un dictionnaire:

return d.iteritems()

qui renvoie un itérateur non trié pour un dictionnaire donné. Je voudrais retourner un itérateur qui passe par les éléments triés par clé . Comment je fais ça?

Mike
la source

Réponses:

171

Je n'ai pas testé cela très largement, mais fonctionne 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 vous avez l'habitude de faire à la for key, value in d.iteritems(): ...place des itérateurs, cela fonctionnera toujours avec la solution ci-dessus

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

Avec Python 3.x, utilisez d.items()au lieu de d.iteritems()pour renvoyer un itérateur.

jpp
la source
29
utilisez à la .items()place de iteritems(): comme l'a dit @Claudiu, iteritems ne fonctionne pas pour Python 3.x, mais items()est disponible à partir de Python 2.6.
Remi
40
Ce n'est pas évident. En fait, items()crée une liste et utilise donc de la mémoire, alors iteritems()qu'essentiellement n'utilise pas de mémoire. Les éléments à utiliser dépendent principalement de la taille du dictionnaire. De plus, l'outil de conversion automatique Python 2 vers Python 3 ( 2to3) prend automatiquement en charge la conversion de iteritems()vers items(), il n'y a donc pas lieu de s'inquiéter à ce sujet.
Eric O Lebigot
5
@HowerHell utilise un collections.OrderedDictpuis vous triez une fois et obtenez toujours les articles dans l'ordre trié.
Mark Harviston
9
Mais @EOL, même s'il iteritems()n'utilise pas de mémoire, tout doit être mis en mémoire pour sorted(), donc il n'y a pas de différence entre l'utilisation de items()et iteritems()ici en termes de mémoire.
Richard
8
@Richard: S'il est vrai que tous les éléments doivent être extraits en mémoire, ils sont stockés deux fois avec items()(dans la liste retournée par items(), et dans la liste triée) et une seule fois avec iteritems()(dans la liste triée uniquement).
Eric O Lebigot
83

Utilisez la sorted()fonction:

return sorted(dict.iteritems())

Si vous voulez un véritable itérateur sur les résultats triés, puisque sorted()renvoie une liste, utilisez:

return iter(sorted(dict.iteritems()))
Greg Hewgill
la source
Cela échoue pour moi: <type 'exceptions.TypeError'>: iter () a renvoyé un non-itérateur de type 'list'
mike
C'est probablement parce que vous utilisez "dict" comme nom de variable. "dict" est en fait le nom de type des dictionnaires. Utilisez simplement un autre nom comme "mydict" ici et le tour est joué.
utku_karatas
1
Ne fonctionne toujours pas. Êtes-vous positif trié () renvoie un autre itérateur, par opposition à une liste régulière?
Mike
quand et où cette exception se produit-elle? vous pouvez parcourir une liste sans problème
1
D'accord, hop. Je ne pense pas que j'appelle directement .next () sauf lors du saut de lignes dans les fichiers. Notre solution iter (sorted (dict.iteritems ())) finit par faire une copie de tout le dict en mémoire au stade "
39

Les clés d'un dict sont stockées dans une table de hachage de sorte que soit leur «ordre naturel», c'est-à-dire pseudo-aléatoire. Toute autre commande est un concept du consommateur du dict.

sorted () renvoie toujours une liste, pas un dict. Si vous lui passez un dict.items () (qui produit une liste de tuples), il retournera une liste de tuples [(k1, v1), (k2, v2), ...] qui peuvent être utilisés dans une boucle d'une manière très semblable à un dict, mais ce n'est en aucun cas 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)]

Ce qui suit ressemble à un dict dans une boucle, mais ce n'est pas le cas, c'est une liste de tuples décompressés dans k, v:

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

À peu près équivalent à:

for k in sorted(foo.keys()):
    print k, foo[k]
Peter Rowell
la source
D'accord, mais je ne veux pas de dict ou de liste, je veux un itérateur. Comment le contraindre à devenir un itérateur?
Mike
2
sorted(foo.keys())est mieux que l'équivalent sorted(foo), car les dictionnaires renvoient leurs clés lorsqu'ils sont itérés (avec l'avantage de ne pas être forcés de créer la foo.keys()liste intermédiaire, peut-être - selon la façon dont sorted()est implémenté les itérables).
Eric O Lebigot
Je me demande ce qui est mieux pour la vitesse et / ou la mémoire k in sorted(foo.keys()):qui tire les touches ou for k,v in sorted(foo.items()):qui renvoie une copie des paires de listes du dictionnaire, je supposesorted(foo.keys())
CrandellWS
1
@CrandellWS: La meilleure façon de répondre à la question de l'heure est avec le module timeit Python .
Peter Rowell
1
@frank - Réponse courte: Non. Un dict est un tableau dont la clé réelle est un hachage de la valeur de la clé fournie. Bien que certaines implémentations puissent être assez prévisibles, et certaines puissent même faire ce contrat, je ne compte sur rien quand il s'agit de la commande de hachage. Voir cet article pour en savoir plus sur le comportement 3.6+. Notez en particulier la première réponse.
Peter Rowell
31

La réponse de Greg est juste. Notez qu'en Python 3.0, vous devrez faire

sorted(dict.items())

comme iteritemssera disparu.

Claudiu
la source
Cela échoue pour moi: <type 'exceptions.TypeError'>: iter () a renvoyé un non-itérateur de type 'list'
mike
3
"Ne pas utiliser de voitures car à l'avenir nous aurons des hoverboards"
JJ
7

Vous pouvez désormais également utiliser OrderedDictPython 2.7:

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

Vous avez ici la page Quoi de neuf pour la version 2.7 et l' API OrderedDict .

Caumons
la source
Cela renverra la clé, les valeurs dans l'ordre où elles sont insérées - pas dans un ordre trié (c'est-à-dire alphabétique).
Tony Suffolk 66
5

En général, on peut trier un dict comme ceci:

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

Pour le cas spécifique de la question, ayant un "drop in replacement" pour d.iteritems (), ajoutez une fonction comme:

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

et donc la ligne de fin change de

return dict.iteritems()

à

return sortdict(dict)

ou

return sortdict(dict, reverse = True)
pythonlarry
la source
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)

Cette méthode a toujours un tri O (N log N), cependant, après une courte segmentation linéaire, elle produit les éléments dans l'ordre trié au fur et à mesure, ce qui la rend théoriquement plus efficace lorsque vous n'avez pas toujours besoin de toute la liste.

jamylak
la source
4

Si vous souhaitez trier par ordre d'insertion des éléments au lieu de l'ordre des clés, vous devriez jeter un œil aux collections de Python . (Python 3 uniquement)

gecco
la source
3

trié renvoie une liste, d'où votre erreur lorsque vous essayez de le parcourir, mais comme vous ne pouvez pas commander un dict, vous devrez traiter une liste.

Je n'ai aucune idée du contexte plus large de votre code, mais vous pouvez essayer d'ajouter un itérateur à la liste résultante. comme ça peut-être?:

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

bien sûr, vous récupérerez les tuples maintenant car triés a transformé votre dict en une liste de tuples

ex: dire que votre dict était: {'a':1,'c':3,'b':2} trié le transforme en une liste:

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

Ainsi, lorsque vous parcourez la liste, vous obtenez (dans cet exemple) un tuple composé d'une chaîne et d'un entier, mais au moins vous pourrez le parcourir.

pcn
la source
2

En supposant que vous utilisez CPython 2.x et que vous disposez d'un grand dictionnaire mydict, l'utilisation de tri (mydict) va être lente car triée crée une liste triée des clés de mydict.

Dans ce cas, vous voudrez peut-être regarder mon paquet ordonné qui comprend une implémentation C de sorteddicten C. Surtout si vous devez parcourir la liste triée de clés plusieurs fois à différentes étapes (c'est-à-dire le nombre d'éléments) de la durée de vie des dictionnaires.

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

Anthon
la source