Dengan Python, bagaimana saya mengulangi kamus dengan urutan kunci yang diurutkan?

211

Ada fungsi yang sudah ada yang berakhir sebagai berikut, di mana dkamus:

return d.iteritems()

yang mengembalikan iterator yang tidak disortir untuk kamus yang diberikan. Saya ingin mengembalikan iterator yang melewati item yang diurutkan berdasarkan kunci . Bagaimana aku melakukan itu?

mike
sumber

Jawaban:

171

Belum menguji ini dengan sangat luas, tetapi bekerja dengan 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)
>>>

Jika Anda terbiasa melakukan for key, value in d.iteritems(): ...alih - alih iterator, ini masih akan bekerja dengan solusi di atas

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

Dengan Python 3.x, gunakan d.items()alih-alih d.iteritems()mengembalikan iterator.

jpp
sumber
29
gunakan .items()alih-alih iteritems(): seperti yang dikatakan @Claudiu, iteritems tidak berfungsi untuk Python 3.x, tetapi items()tersedia dari Python 2.6.
Remi
40
Ini tidak jelas. Bahkan, items()membuat daftar dan karenanya menggunakan memori, sedangkan iteritems()pada dasarnya tidak menggunakan memori. Apa yang digunakan sebagian besar tergantung pada ukuran kamus. Selain itu, alat konversi Python 2 ke Python 3 2to3otomatis ( ) secara otomatis menangani konversi dari iteritems()ke items(), sehingga tidak perlu khawatir tentang hal ini.
Eric O Lebigot
5
@HowerHell gunakan a collections.OrderedDictlalu Anda mengurutkan sekali & mendapatkan item dalam urutan diurutkan selalu.
Mark Harviston
9
Tapi @EOL, bahkan jika iteritems()tidak menggunakan memori, semuanya harus ditarik ke dalam memori sorted(), jadi tidak ada perbedaan antara penggunaan items()dan di iteritems()sini dari segi memori.
Richard
8
@ Richard: Meskipun benar bahwa semua elemen harus ditarik ke dalam memori, mereka disimpan dua kali dengan items()(dalam daftar dikembalikan oleh items(), dan dalam daftar diurutkan) dan hanya sekali dengan iteritems()(dalam daftar diurutkan saja).
Eric O Lebigot
83

Gunakan sorted()fungsinya:

return sorted(dict.iteritems())

Jika Anda ingin iterator aktual atas hasil yang diurutkan, sejak sorted()mengembalikan daftar, gunakan:

return iter(sorted(dict.iteritems()))
Greg Hewgill
sumber
Itu gagal untuk saya: <type 'exceptionions.TypeError'>: iter () mengembalikan non-iterator dari tipe 'list'
mike
Itu mungkin karena Anda menggunakan "dict" sebagai nama variabel. "dict" sebenarnya adalah nama jenis kamus. Cukup gunakan nama lain seperti "mydict" di sini dan voila.
utku_karatas
1
Masih tidak bekerja. Apakah Anda positif diurutkan () mengembalikan iterator lain, sebagai lawan dari daftar reguler?
mike
kapan dan di mana pengecualian ini terjadi? Anda dapat mengulangi daftar tanpa masalah
1
Setuju, hop. Saya tidak berpikir saya pernah memanggil .next () secara langsung kecuali ketika melewatkan baris dalam file. Solusi iter (sort (dict.iteritems ())) kami akhirnya membuat salinan seluruh dikt dalam memori pada tahap "disortir (", jadi manfaat iterator utama tampaknya hilang :)
39

Kunci dikt disimpan dalam hashtable sehingga merupakan 'tatan alami' mereka, yaitu psuedo-random. Pemesanan lainnya adalah konsep konsumen dikt.

diurutkan () selalu mengembalikan daftar, bukan dict. Jika Anda memberikannya dict.items () (yang menghasilkan daftar tupel), itu akan mengembalikan daftar tupel [(k1, v1), (k2, v2), ...] yang dapat digunakan dalam satu lingkaran dengan cara yang sangat mirip dengan dict, tetapi itu tidak berarti pula 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)]

Berikut ini terasa seperti dict dalam satu lingkaran, tetapi bukan, itu adalah daftar tuple yang dibongkar menjadi k, v:

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

Setara dengan:

for k in sorted(foo.keys()):
    print k, foo[k]
Peter Rowell
sumber
Oke, tapi saya tidak ingin Dict atau Daftar, saya ingin Iterator. Bagaimana cara saya memaksanya untuk menjadi seorang Iterator?
mike
2
sorted(foo.keys())lebih baik sebagai padanan sorted(foo), karena kamus mengembalikan kunci mereka ketika iterasi (dengan keuntungan tidak dipaksa untuk membuat foo.keys()daftar perantara, mungkin — tergantung pada bagaimana sorted()diterapkan untuk iterables).
Eric O Lebigot
Bertanya-tanya mana yang lebih baik untuk kecepatan dan / atau memori k in sorted(foo.keys()):yang menarik kunci atau for k,v in sorted(foo.items()):yang mengembalikan salinan pasangan daftar kamus yang kurasasorted(foo.keys())
CrandellWS
1
@CrandellWS: Cara terbaik untuk menjawab pertanyaan waktu adalah dengan modul Python timeit .
Peter Rowell
1
@ jujur ​​- Jawaban Singkat: Tidak. Dict adalah larik dengan kunci aktual menjadi hash dari nilai kunci yang disediakan. Meskipun beberapa implementasi mungkin cukup dapat diprediksi, dan beberapa bahkan dapat membuat kontrak ini, saya tidak mengandalkan apa pun ketika datang ke pemesanan hash. Lihat posting ini untuk informasi lebih lanjut tentang perilaku 3.6+. Perhatikan jawaban pertama.
Peter Rowell
31

Jawaban Greg benar. Perhatikan bahwa dalam Python 3.0 Anda harus melakukannya

sorted(dict.items())

seperti yang iteritemsakan hilang.

Claudiu
sumber
Itu gagal untuk saya: <type 'exceptionions.TypeError'>: iter () mengembalikan non-iterator dari tipe 'list'
mike
3
"Jangan menggunakan mobil karena di masa depan kita akan memiliki hoverboard"
JJ
7

Anda sekarang dapat menggunakan OrderedDictPython 2.7 juga:

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

Di sini Anda memiliki halaman apa yang baru untuk versi 2.7 dan API OrderedDict .

Caumons
sumber
Itu akan mengembalikan kunci, nilai-nilai dalam urutan yang dimasukkan - bukan dalam urutan yang diurutkan (yaitu alfabet).
Tony Suffolk 66
5

Secara umum, seseorang dapat mengurutkan dict seperti:

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

Untuk kasus spesifik dalam pertanyaan, memiliki "drop in replacement" untuk d.iteritems (), tambahkan fungsi seperti:

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

dan garis akhir berubah dari

return dict.iteritems()

untuk

return sortdict(dict)

atau

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

Metode ini masih memiliki semacam O (N log N), namun, setelah heapify linier pendek, ia menghasilkan item dalam urutan diurutkan saat berjalan, membuatnya secara teoritis lebih efisien ketika Anda tidak selalu membutuhkan seluruh daftar.

jamylak
sumber
4

Jika Anda ingin mengurutkan berdasarkan urutan item yang dimasukkan dan bukan urutan kunci, Anda harus melihat ke koleksi Python .OrderedDict . (Hanya Python 3)

gecco
sumber
3

diurutkan mengembalikan daftar, maka kesalahan Anda ketika Anda mencoba untuk mengulanginya, tetapi karena Anda tidak dapat memesan dict Anda harus berurusan dengan daftar.

Saya tidak tahu apa konteks kode Anda yang lebih besar, tetapi Anda bisa mencoba menambahkan iterator ke daftar yang dihasilkan. seperti ini mungkin ?:

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

tentu saja Anda akan mendapatkan kembali tuple sekarang karena diurutkan mengubah dict Anda menjadi daftar tuple

mis: katakanlah dikt Anda: {'a':1,'c':3,'b':2} diurutkan mengubahnya menjadi daftar:

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

jadi ketika Anda benar-benar mengulangi daftar Anda kembali (dalam contoh ini) sebuah tupel yang terdiri dari string dan integer, tetapi setidaknya Anda akan dapat beralih di atasnya.

pcn
sumber
2

Dengan asumsi Anda menggunakan CPython 2.x dan memiliki mydict kamus besar, maka menggunakan sortir (mydict) akan menjadi lambat karena disortir membuat daftar kunci-kunci mydict yang diurutkan.

Dalam hal ini Anda mungkin ingin melihat paket ddt saya yang memuat implementasi C dari sorteddict di C. Terutama jika Anda harus memeriksa daftar kunci yang diurutkan beberapa kali pada tahap yang berbeda (mis. Jumlah elemen) dari masa kamus.

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

Anthon
sumber