No Python, como iterar em um dicionário em ordem de chave classificada?

211

Existe uma função existente que termina no seguinte, onde destá um dicionário:

return d.iteritems()

que retorna um iterador não classificado para um determinado dicionário. Gostaria de retornar um iterador que percorre os itens classificados por chave . Como faço isso?

Mike
fonte

Respostas:

171

Não testou isso muito extensivamente, mas funciona no 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)
>>>

Se você está acostumado a fazer, em for key, value in d.iteritems(): ...vez de iteradores, isso ainda funcionará com a solução acima

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

Com o Python 3.x, use em d.items()vez de d.iteritems()retornar um iterador.

jpp
fonte
29
use em .items()vez de iteritems(): como o @Claudiu disse, os iteritems não funcionam no Python 3.x, mas items()estão disponíveis no Python 2.6.
Remi
40
Isto não é óbvio. De fato, items()cria uma lista e, portanto, usa memória, enquanto que iteritems()essencialmente não usa memória. O que usar depende principalmente do tamanho do dicionário. Além disso, a ferramenta de conversão automática Python 2 para Python 3 ( 2to3) cuida automaticamente da conversão de iteritems()para items(), então não há necessidade de se preocupar com isso.
Eric O Lebigot
5
@HowerHell use a, collections.OrderedDictentão você classifica uma vez e obtém os itens sempre na ordem de classificação.
precisa saber é o seguinte
9
Mas o @EOL, mesmo iteritems()que não use memória, tudo deve ser puxado para a memória sorted(), para que não haja diferença entre o uso da memória items()e iteritems()aqui.
Richard
8
@ Richard: Embora seja verdade que todos os elementos devem ser puxados para a memória, eles são armazenados duas vezes com items()(na lista retornada por items()e na lista classificada) e apenas uma vez com iteritems()(apenas na lista classificada).
Eric O Lebigot
83

Use a sorted()função:

return sorted(dict.iteritems())

Se você deseja um iterador real sobre os resultados classificados, pois sorted()retorna uma lista, use:

return iter(sorted(dict.iteritems()))
Greg Hewgill
fonte
Isso falha para mim: <type 'exceptions.TypeError'>: iter () retornou um não-iterador do tipo 'list'
mike
Provavelmente porque você usa "dict" como o nome da variável. "dict" é realmente o nome do tipo dos dicionários. Basta usar outro nome como "mydict" aqui e pronto.
utku_karatas
1
Ainda não funciona. Você está classificado com positivo () retorna outro iterador, em oposição a uma lista regular?
mike
quando e onde ocorre essa exceção? você pode iterar sobre uma lista sem problemas
1
Concordo, hop. Acho que nunca chamo .next () diretamente, exceto quando pulo linhas nos arquivos. Nossos iter (ordenadas (dict.iteritems ())) extremidades solução acima de fazer uma cópia de todo o dict na memória no "(classificadas" estágio de qualquer maneira, de modo que o benefício iterador principal parece perdido :)
39

As chaves de um ditado são armazenadas em uma hashtable, de modo que essa é sua 'ordem natural', isto é, psuedo-random. Qualquer outra ordem é um conceito do consumidor do ditado.

classificado () sempre retorna uma lista, não um ditado. Se você passar um dict.items () (que produz uma lista de tuplas), ele retornará uma lista de tuplas [(k1, v1), (k2, v2), ...] que podem ser usadas em um loop de uma maneira muito parecida com um ditado, mas de modo algum é um ditado !

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

A seguir, parece um ditado em um loop, mas não é, é uma lista de tuplas sendo descompactadas em 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
fonte
Ok, mas não quero um ditado ou uma lista, quero um iterador. Como coagi-lo a ser um Iterador?
mike
2
sorted(foo.keys())é melhor que o equivalente sorted(foo), já que os dicionários retornam suas chaves quando iterados (com a vantagem de não serem forçados a criar a foo.keys()lista intermediária, talvez - dependendo de como sorted()é implementado para iterables).
Eric O Lebigot
Maravilha o que é melhor para a velocidade e / ou a memória k in sorted(foo.keys()):que puxa as chaves ou for k,v in sorted(foo.items()):que retorna uma cópia da lista de pares do dicionário eu achosorted(foo.keys())
CrandellWS
1
@CrandellWS: A melhor maneira de responder à questão do tempo é com o módulo timeit do Python .
22416 Peter
1
@frank - Resposta curta: Não. Um dict é uma matriz com a chave real sendo um hash do valor da chave fornecida. Embora algumas implementações possam ser razoavelmente previsíveis e outras possam até fazer esse contrato, não conto com nada quando se trata de pedidos de hash. Veja este post para saber mais sobre o comportamento 3.6+. Em particular, observe a primeira resposta.
Peter Rowell
31

A resposta de Greg está certa. Observe que no Python 3.0 você terá que fazer

sorted(dict.items())

como iteritemsse foi.

Claudiu
fonte
Isso falha para mim: <type 'exceptions.TypeError'>: iter () retornou um não iterador do tipo 'list'
mike
3
"Não faça uso de carros, porque no futuro teremos hoverboards"
JJ
7

Agora você também pode usar o OrderedDictPython 2.7:

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

Aqui você tem a nova que é página para 2,7 versão ea API OrderedDict .

Caumons
fonte
Isso retornará chave, valores na ordem em que são inseridos - não em uma ordem classificada (ou seja, alfabética).
Tony Suffolk 66
5

Em geral, pode-se classificar um ditado assim:

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

Para o caso específico da pergunta, com uma "queda na substituição" de d.iteritems (), adicione uma função 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]

e assim a linha final muda de

return dict.iteritems()

para

return sortdict(dict)

ou

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

Esse método ainda possui uma classificação O (N log N); no entanto, após um pequeno heapify linear, ele produz os itens na ordem de classificação conforme o andamento, tornando-o teoricamente mais eficiente quando você nem sempre precisa da lista inteira.

jamylak
fonte
4

Se você deseja classificar pela ordem em que os itens foram inseridos, em vez da ordem das chaves, consulte as coleções do Python.OrderedDict . (Somente Python 3)

gecco
fonte
3

classificado retorna uma lista, daí o seu erro ao tentar iterá-lo, mas como você não pode ordenar um ditado, precisará lidar com uma lista.

Não faço ideia do contexto maior do seu código, mas você pode tentar adicionar um iterador à lista resultante. assim talvez ?:

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

é claro que você receberá de volta as tuplas agora porque classificadas transformou seu ditado em uma lista de tuplas

ex: diga que seu ditado era: {'a':1,'c':3,'b':2} classificado transforma-o em uma lista:

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

portanto, quando você realmente itera sobre a lista, recebe (neste exemplo) uma tupla composta por uma string e um número inteiro, mas pelo menos você poderá iterar sobre ela.

pcn
fonte
2

Supondo que você esteja usando o CPython 2.xe possua um grande dicionário mydict, o uso de ordenado (mydict) será lento, porque ordenado cria uma lista ordenada das chaves do mydict.

Nesse caso, você pode querer olhar para o meu pacote ordersdict, que inclui uma implementação C de sorteddict em C. Especialmente se você precisar a lista classificada de chaves várias vezes em diferentes estágios (ou seja, número de elementos) da vida útil dos dicionários.

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

Anthon
fonte