Apa cara terbaik untuk beralih ke kamus?

2628

Saya telah melihat beberapa cara berbeda untuk beralih ke kamus di C #. Apakah ada cara standar?

Jake Stewart
sumber
108
Saya terkejut ada banyak jawaban untuk pertanyaan ini, bersama dengan 923 kali diputuskan .. (menggunakan foreach ofcourse) .. Saya akan berdebat, atau setidaknya menambahkan bahwa jika Anda harus beralih ke kamus, kemungkinan Anda adalah menggunakannya dengan salah / tidak tepat .. Saya harus membuat komentar ini, karena saya telah melihat kamus disalahgunakan dengan cara yang IMHO tidak sesuai ... Ya bisa ada keadaan langka ketika Anda beralih ke kamus, daripada mencari, yang adalah apa itu dirancang untuk .. Harap diingat, sebelum bertanya-tanya bagaimana cara beralih ke kamus.
Vikas Gupta
74
@VikasGupta Apa yang akan Anda sarankan untuk melakukan sesuatu dengan koleksi pasangan nilai kunci ketika Anda tidak tahu apa yang akan menjadi kunci?
nasch
26
@displayName Jika Anda ingin melakukan sesuatu dengan setiap pasangan nilai kunci tetapi tidak memiliki referensi ke tombol yang digunakan untuk mencari nilai, Anda akan beralih ke kamus, kan? Saya baru saja menunjukkan bahwa mungkin ada saat-saat Anda ingin melakukan itu, meskipun Vikas mengklaim bahwa ini biasanya penggunaan yang salah.
nasch
35
Untuk mengatakan bahwa itu adalah penggunaan yang salah berarti ada alternatif yang lebih baik. Apa alternatifnya?
Kyle Delaney
41
VikasGupta salah, saya dapat menegaskan bahwa setelah bertahun-tahun pemrograman C # dan C ++ berkinerja tinggi dalam skenario non-teoritis. Memang sering ada kasus di mana seseorang akan membuat kamus, menyimpan pasangan nilai kunci yang unik, dan kemudian mengulangi nilai-nilai ini, yang terbukti memiliki kunci unik dalam koleksi. Membuat koleksi lebih lanjut adalah cara yang sangat tidak efisien dan mahal untuk menghindari iterasi kamus. Berikan alternatif yang baik sebagai jawaban atas pertanyaan yang menjelaskan sudut pandang Anda, jika tidak, komentar Anda tidak masuk akal.
sɐunıɔ ןɐ qɐp

Jawaban:

3715
foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}
Pablo Fernandez
sumber
29
Bagaimana jika saya tidak tahu persis jenis kunci / nilai dalam Kamus. Menggunakan var entrylebih baik dalam hal itu, dan dengan demikian saya memilih jawaban ini pada tampilan kedua daripada yang di atas.
Ozair Kafray
93
@OzairKafray menggunakan varketika Anda tidak tahu jenisnya adalah praktik yang buruk.
Nate
52
Jawaban ini lebih unggul karena Pablo tidak secara default menggunakan lazy coder "var" yang mengaburkan jenis pengembalian.
MonkeyWrench
119
@MonkeyWrench: Meh. Visual Studio tahu apa tipenya; yang harus Anda lakukan adalah mengarahkan variabel untuk mencari tahu.
Robert Harvey
31
Seperti yang saya pahami, varhanya berfungsi jika jenisnya diketahui pada waktu kompilasi. Jika Visual Studio mengetahui jenisnya maka tersedia bagi Anda untuk mengetahuinya juga.
Kyle Delaney
866

Jika Anda mencoba menggunakan Kamus generik dalam C # seperti Anda akan menggunakan array asosiatif dalam bahasa lain:

foreach(var item in myDictionary)
{
  foo(item.Key);
  bar(item.Value);
}

Atau, jika Anda hanya perlu mengulangi pengumpulan kunci, gunakan

foreach(var item in myDictionary.Keys)
{
  foo(item);
}

Dan terakhir, jika Anda hanya tertarik pada nilai-nilai:

foreach(var item in myDictionary.Values)
{
  foo(item);
}

(Perhatikan bahwa varkata kunci adalah fitur C # 3.0 opsional dan di atas, Anda juga dapat menggunakan jenis kunci / nilai yang tepat di sini)

Yakub
sumber
11
fitur var paling diperlukan untuk blok kode pertama Anda :)
nawfal
27
Saya menghargai bahwa jawaban ini menunjukkan Anda dapat mengulangi kunci atau nilai secara eksplisit.
Rotsiser Mho
11
Saya tidak suka penggunaan var di sini. Mengingat itu hanya gula sintaksis, mengapa menggunakannya di sini? Ketika seseorang mencoba membaca kode, mereka harus melompat-lompat kode untuk menentukan jenis myDictionary(kecuali itu adalah nama sebenarnya tentu saja). Saya pikir menggunakan var baik ketika jenisnya jelas misalnya var x = "some string"tetapi ketika tidak segera jelas saya pikir itu kode malas yang menyakitkan pembaca kode / resensi
James Wierzba
8
varharus digunakan hemat, menurut pendapat saya. Khususnya di sini, itu tidak konstruktif: jenisnya KeyValuePairmungkin relevan dengan pertanyaan.
Sinjai
3
varmemiliki tujuan yang unik dan saya tidak percaya itu adalah gula 'sintaksis'. Menggunakannya dengan sengaja adalah pendekatan yang tepat.
Joshua K
159

Dalam beberapa kasus, Anda mungkin memerlukan penghitung yang mungkin disediakan oleh implementasi for-loop. Untuk itu, LINQ menyediakan ElementAtyang memungkinkan berikut ini:

for (int index = 0; index < dictionary.Count; index++) {
  var item = dictionary.ElementAt(index);
  var itemKey = item.Key;
  var itemValue = item.Value;
}
Maurício Fedatto
sumber
21
Untuk menggunakan metode '.ElementAt', ingat: using System.Linq; Ini tidak termasuk dalam fx. kelas tes yang dihasilkan secara otomatis.
Tinia
14
Ini adalah cara untuk pergi jika Anda memodifikasi nilai yang terkait dengan kunci. Kalau tidak, pengecualian dilemparkan saat memodifikasi dan menggunakan foreach ().
Mike de Klerk
27
Bukankah ElementAtoperasi O (n)?
Arturo Torres Sánchez
162
Jawaban ini benar-benar tidak layak dari begitu banyak upvotes. Kamus tidak memiliki urutan tersirat, jadi penggunaan .ElementAtdalam konteks ini dapat menyebabkan bug halus. Jauh lebih serius adalah poin Arturo di atas. Anda akan mengulangi waktu kamus yang dictionary.Count + 1mengarah ke kompleksitas O (n ^ 2) untuk operasi yang seharusnya hanya O (n). Jika Anda benar-benar membutuhkan indeks (jika perlu, Anda mungkin menggunakan jenis koleksi yang salah sejak awal), Anda harus mengulanginya dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})dan tidak menggunakan .ElementAtdi dalam loop.
pemboros
5
Operasi ElementAt - o (n)! Serius? Ini adalah contoh bagaimana Anda seharusnya tidak melakukannya. Banyak upvote ini?
Mukesh Adhvaryu
96

Tergantung pada apakah Anda setelah kunci atau nilai ...

Dari Dictionary(TKey, TValue)deskripsi Kelas MSDN :

// When you use foreach to enumerate dictionary elements,
// the elements are retrieved as KeyValuePair objects.
Console.WriteLine();
foreach( KeyValuePair<string, string> kvp in openWith )
{
    Console.WriteLine("Key = {0}, Value = {1}", 
        kvp.Key, kvp.Value);
}

// To get the values alone, use the Values property.
Dictionary<string, string>.ValueCollection valueColl =
    openWith.Values;

// The elements of the ValueCollection are strongly typed
// with the type that was specified for dictionary values.
Console.WriteLine();
foreach( string s in valueColl )
{
    Console.WriteLine("Value = {0}", s);
}

// To get the keys alone, use the Keys property.
Dictionary<string, string>.KeyCollection keyColl =
    openWith.Keys;

// The elements of the KeyCollection are strongly typed
// with the type that was specified for dictionary keys.
Console.WriteLine();
foreach( string s in keyColl )
{
    Console.WriteLine("Key = {0}", s);
}
J Healy
sumber
82

Secara umum, meminta "cara terbaik" tanpa konteks tertentu seperti bertanya apa warna terbaik ?

Di satu sisi, ada banyak warna dan tidak ada warna terbaik. Itu tergantung pada kebutuhan dan sering juga pada rasa.

Di sisi lain, ada banyak cara untuk beralih ke Kamus di C # dan tidak ada cara terbaik. Itu tergantung pada kebutuhan dan sering juga pada rasa.

Cara yang paling mudah

foreach (var kvp in items)
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

Jika Anda hanya membutuhkan nilainya (memungkinkan untuk memanggilnya item, lebih dapat dibaca daripada kvp.Value).

foreach (var item in items.Values)
{
    doStuff(item)
}

Jika Anda memerlukan urutan pengurutan tertentu

Secara umum, pemula terkejut tentang urutan enumerasi suatu Kamus.

LINQ menyediakan sintaksis ringkas yang memungkinkan untuk menentukan pesanan (dan banyak hal lainnya), misalnya:

foreach (var kvp in items.OrderBy(kvp => kvp.Key))
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

Sekali lagi Anda mungkin hanya perlu nilai. LINQ juga menyediakan solusi ringkas untuk:

  • iterate langsung pada nilai (memungkinkan untuk menyebutnya item, lebih mudah dibaca daripada kvp.Value)
  • tetapi diurutkan berdasarkan tombol

Ini dia:

foreach (var item in items.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value))
{
    doStuff(item)
}

Ada banyak lagi kasus penggunaan dunia nyata yang dapat Anda lakukan dari contoh-contoh ini. Jika Anda tidak memerlukan pesanan khusus, cukup ikuti "cara paling mudah" (lihat di atas)!

Stéphane Gourichon
sumber
Yang terakhir seharusnya .Valuesdan bukan klausa pilih.
Mafii
@Mafii Anda yakin? Nilai yang dikembalikan oleh OrderBy bukan dari tipe KeyValuePair, mereka tidak memiliki Valuebidang. Jenis yang tepat saya lihat di sini adalah IOrderedEnumerable<KeyValuePair<TKey, TValue>>. Mungkin Anda bermaksud sesuatu yang lain? Bisakah Anda menulis baris lengkap yang menunjukkan apa yang Anda maksud (dan mengujinya)?
Stéphane Gourichon
Saya pikir jawaban ini mengandung apa yang saya maksud: stackoverflow.com/a/141105/5962841 tetapi koreksi saya jika saya bingung sesuatu
Mafii
1
@Mafii Baca kembali seluruh jawaban saya, penjelasan di antara bagian kode memberi tahu konteksnya. Jawaban yang Anda sebutkan adalah seperti bagian kode kedua dalam jawaban saya (tidak perlu memesan). Di sana saya hanya menulis items.Valueseperti yang Anda sarankan. Dalam kasus bagian keempat yang Anda komentari, Select()ini adalah cara untuk menyebabkan foreachpenghitungan langsung pada nilai-nilai dalam kamus alih-alih pasangan nilai kunci. Jika Anda tidak suka Select()dalam hal ini, Anda mungkin lebih suka bagian kode ketiga. Inti dari bagian keempat adalah untuk menunjukkan bahwa seseorang dapat melakukan pra-proses pengumpulan dengan LINQ.
Stéphane Gourichon
1
Jika Anda melakukannya, .Keys.Orderby()Anda akan mengulanginya pada daftar kunci. Jika itu yang Anda butuhkan, baiklah. Jika Anda membutuhkan nilai, maka dalam loop Anda harus menanyakan kamus pada setiap tombol untuk mendapatkan nilai. Dalam banyak skenario itu tidak akan membuat perbedaan praktis. Dalam skenario kinerja tinggi, itu akan terjadi. Seperti yang saya tulis di awal jawaban: "ada banyak cara (...) dan tidak ada cara terbaik. Itu tergantung pada kebutuhan dan sering juga pada selera."
Stéphane Gourichon
53

Saya akan mengatakan foreach adalah cara standar, meskipun itu jelas tergantung pada apa yang Anda cari

foreach(var kvp in my_dictionary) {
  ...
}

Itukah yang kamu cari?

George Mauer
sumber
42
Um, bukankah menyebut item "nilai" agak membingungkan? Anda biasanya akan menggunakan sintaksis seperti "value.Key" dan "value.Value", yang tidak terlalu intuitif untuk orang lain yang akan membaca kode, terutama jika mereka tidak terbiasa dengan bagaimana .Net Dictionary diimplementasikan .
RenniePet
3
@RenniePet kvpumumnya digunakan untuk nama KeyValuePair contoh ketika iterasi kamus dan struktur data terkait: foreach(var kvp in myDictionary){....
mbx
37

Anda juga dapat mencoba ini pada kamus besar untuk pemrosesan multithreaded.

dictionary
.AsParallel()
.ForAll(pair => 
{ 
    // Process pair.Key and pair.Value here
});
Onur
sumber
8
@WiiMaxx dan yang lebih penting jika barang-barang ini TIDAK saling bergantung
Mafii
37

C # 7.0 memperkenalkan Deconstructors dan jika Anda menggunakan .NET Core 2.0+ Application, structKeyValuePair<>sudah menyertakan aDeconstruct()untuk Anda. Jadi kamu bisa melakukan:

var dic = new Dictionary<int, string>() { { 1, "One" }, { 2, "Two" }, { 3, "Three" } };
foreach (var (key, value) in dic) {
    Console.WriteLine($"Item [{key}] = {value}");
}
//Or
foreach (var (_, value) in dic) {
    Console.WriteLine($"Item [NO_ID] = {value}");
}
//Or
foreach ((int key, string value) in dic) {
    Console.WriteLine($"Item [{key}] = {value}");
}

masukkan deskripsi gambar di sini

Jaider
sumber
5
Jika Anda menggunakan .NET Framework, yang setidaknya hingga 4.7.2 tidak memiliki Dekonstruksi pada KeyValuePair, coba ini:foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
nwsmith
29

Saya menghargai pertanyaan ini sudah memiliki banyak tanggapan tetapi saya ingin melakukan sedikit riset.

Iterasi pada kamus bisa agak lambat jika dibandingkan dengan iterasi pada sesuatu seperti array. Dalam pengujian saya, iterasi pada array membutuhkan waktu 0,015003 detik sedangkan iterasi pada kamus (dengan jumlah elemen yang sama) mengambil 0,0365073 detik, yang 2,4 kali lebih lama! Meskipun saya telah melihat perbedaan yang jauh lebih besar. Sebagai perbandingan, suatu Daftar berada di antara 0,00215043 detik.

Namun, itu seperti membandingkan apel dan jeruk. Maksud saya adalah iterasi kamus lebih lambat.

Kamus dioptimalkan untuk pencarian, jadi dengan itu dalam pikiran saya telah membuat dua metode. Satu hanya melakukan pendahuluan, yang lain iterates kunci kemudian melihat ke atas.

public static string Normal(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var kvp in dictionary)
    {
        value = kvp.Value;
        count++;
    }

    return "Normal";
}

Yang ini memuat kunci dan beralih di atasnya (saya juga mencoba menarik kunci menjadi string [] tetapi perbedaannya dapat diabaikan.

public static string Keys(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var key in dictionary.Keys)
    {
        value = dictionary[key];
        count++;
    }

    return "Keys";
}

Dengan contoh ini tes foreach normal mengambil 0,0310062 dan versi kunci mengambil 0,2205441. Memuat semua kunci dan mengulangi semua pencarian jelas BANYAK lebih lambat!

Untuk tes akhir saya telah melakukan iterasi saya sepuluh kali untuk melihat apakah ada manfaat menggunakan kunci di sini (pada titik ini saya hanya ingin tahu):

Inilah metode RunTest jika itu membantu Anda memvisualisasikan apa yang terjadi.

private static string RunTest<T>(T dictionary, Func<T, string> function)
{            
    DateTime start = DateTime.Now;
    string name = null;
    for (int i = 0; i < 10; i++)
    {
        name = function(dictionary);
    }
    DateTime end = DateTime.Now;
    var duration = end.Subtract(start);
    return string.Format("{0} took {1} seconds", name, duration.TotalSeconds);
}

Di sini proses normal foreach memakan waktu 0,2820564 detik (sekitar sepuluh kali lebih lama dari iterasi tunggal - seperti yang Anda harapkan). Iterasi pada tombol mengambil 2,2249449 detik.

Diedit Untuk Menambahkan: Membaca beberapa jawaban lain membuat saya mempertanyakan apa yang akan terjadi jika saya menggunakan Kamus dan bukan Kamus. Dalam contoh ini array membutuhkan waktu 0,0120024 detik, daftar 0,0185037 detik dan kamus 0,0465093 detik. Masuk akal untuk berharap bahwa tipe data membuat perbedaan pada seberapa lambat kamus.

Apa Kesimpulan Saya ?

  • Hindari pengulangan pada kamus jika Anda bisa, mereka jauh lebih lambat daripada pengulangan pada array dengan data yang sama di dalamnya.
  • Jika Anda memilih untuk beralih ke kamus jangan mencoba terlalu pintar, meskipun lebih lambat Anda bisa melakukan jauh lebih buruk daripada menggunakan metode standar foreach.
Liath
sumber
11
Anda harus mengukur dengan sesuatu seperti StopWatch alih-alih DateTime: hanselman.com/blog/…
Even Mien
2
bisakah Anda menjelaskan skenario pengujian Anda, berapa banyak item yang ada di kamus Anda, seberapa sering Anda menjalankan skenario Anda untuk menghitung waktu rata-rata, ...
WiiMaxx
3
Menariknya Anda akan mendapatkan hasil yang berbeda tergantung pada data apa yang Anda miliki di kamus. Sementara itterating over the Dictionary, fungsi Enumerator harus melewati banyak slot kosong di kamus yang menyebabkannya lebih lambat daripada iterasi pada array. Jika Kamus penuh, slot kosong yang akan dilewati lebih sedikit daripada jika setengah kosong.
Martin Brown
26

Ada banyak pilihan. Favorit pribadi saya adalah oleh KeyValuePair

Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary)
{
     // Do some interesting things
}

Anda juga dapat menggunakan Koleksi Tombol dan Nilai

theo
sumber
14

Dengan .NET Framework 4.7satu dapat menggunakan dekomposisi

var fruits = new Dictionary<string, int>();
...
foreach (var (fruit, number) in fruits)
{
    Console.WriteLine(fruit + ": " + number);
}

Agar kode ini berfungsi pada versi C # yang lebih rendah, tambahkan System.ValueTuple NuGet packagedan tulis di suatu tempat

public static class MyExtensions
{
    public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> tuple,
        out T1 key, out T2 value)
    {
        key = tuple.Key;
        value = tuple.Value;
    }
}
Pavel
sumber
9
Ini salah. .NET 4.7 hanya memiliki ValueTuplebuilt-in. Ini tersedia sebagai paket nuget untuk versi sebelumnya. Lebih penting lagi, C # 7.0+ diperlukan agar Deconstructmetode ini berfungsi sebagai dekonstruktor var (fruit, number) in fruits.
David Arno
13

Pada C # 7, Anda dapat mendekonstruksi objek menjadi variabel. Saya percaya ini menjadi cara terbaik untuk beralih ke kamus.

Contoh:

Buat metode ekstensi untuk KeyValuePair<TKey, TVal>mendekonstruksinya:

public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey key, out TVal value)
{
   key = pair.Key;
   value = pair.Value;
}

Iterasi Dictionary<TKey, TVal>dengan cara berikut

// Dictionary can be of any types, just using 'int' and 'string' as examples.
Dictionary<int, string> dict = new Dictionary<int, string>();

// Deconstructor gets called here.
foreach (var (key, value) in dict)
{
   Console.WriteLine($"{key} : {value}");
}
Domn Werner
sumber
10

Anda menyarankan di bawah ini untuk beralih

Dictionary<string,object> myDictionary = new Dictionary<string,object>();
//Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary) {
    //Do some interesting things;
}

FYI, foreachtidak berfungsi jika nilainya bertipe objek.

George Stocker
sumber
4
Silakan rumit: foreachtidak akan bekerja jika yang nilai adalah tipe object? Kalau tidak, ini tidak masuk akal.
Marc L.
9

Bentuk paling sederhana untuk beralih ke kamus:

foreach(var item in myDictionary)
{ 
    Console.WriteLine(item.Key);
    Console.WriteLine(item.Value);
}
Ron
sumber
9

Dengan menggunakan C # 7 , tambahkan metode ekstensi ini ke proyek apa pun dari solusi Anda:

public static class IDictionaryExtensions
{
    public static IEnumerable<(TKey, TValue)> Tuples<TKey, TValue>(
        this IDictionary<TKey, TValue> dict)
    {
        foreach (KeyValuePair<TKey, TValue> kvp in dict)
            yield return (kvp.Key, kvp.Value);
    }
}


Dan gunakan sintaksis sederhana ini

foreach (var(id, value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}


Atau yang ini, jika Anda mau

foreach ((string id, object value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}


Di tempat tradisional

foreach (KeyValuePair<string, object> kvp in dict)
{
    string id = kvp.Key;
    object value = kvp.Value;

    // your code using 'id' and 'value'
}


Metode ekstensi mengubah KeyValuePairAnda IDictionary<TKey, TValue>menjadi sangat diketiktuple , memungkinkan Anda untuk menggunakan sintaks nyaman baru ini.

Itu mengkonversi-hanya- entri kamus yang diperlukan untuk tuples, sehingga TIDAK mengubah seluruh kamustuples , sehingga tidak ada masalah kinerja yang terkait dengan itu.

Hanya ada sedikit biaya memanggil metode ekstensi untuk membuat tupledibandingkan dengan menggunakan KeyValuePairlangsung, yang TIDAK boleh menjadi masalah jika Anda menetapkan KeyValuePairproperti KeydanValue variabel loop baru.

Dalam praktiknya, sintaksis baru ini sangat cocok untuk sebagian besar kasus, kecuali untuk skenario kinerja ultra-tinggi tingkat rendah, di mana Anda masih memiliki opsi untuk tidak menggunakannya di tempat tertentu.

Lihat ini: Blog MSDN - Fitur baru di C # 7

sɐunıɔ ןɐ qɐp
sumber
1
Apa yang menjadi alasan untuk memilih tuple 'nyaman' daripada pasangan kunci-nilai? Saya tidak melihat keuntungan di sini. Tuple Anda berisi kunci dan nilai, begitu pula pasangan kunci-nilai.
Maarten
4
Hai Maarten, terima kasih atas pertanyaan Anda. Manfaat utama adalah keterbacaan kode tanpa upaya pemrograman tambahan. Dengan KeyValuePair kita harus selalu menggunakan formulir kvp.Keydan kvp.Valueuntuk menggunakan masing-masing kunci dan nilai. Dengan tuple Anda mendapatkan fleksibilitas untuk memberi nama kunci dan nilainya sesuai keinginan, tanpa menggunakan deklarasi variabel lebih lanjut di dalam blok foreach. Misalnya Anda dapat memberi nama kunci Anda factoryName, dan nilainya sebagai models, yang sangat berguna ketika Anda mendapatkan loop bersarang (kamus kamus): pemeliharaan kode menjadi lebih mudah. Cobalah! ;-)
sɐunıɔ ןɐ qɐp
7

Saya tahu ini adalah pertanyaan yang sangat lama, tetapi saya membuat beberapa metode ekstensi yang mungkin berguna:

    public static void ForEach<T, U>(this Dictionary<T, U> d, Action<KeyValuePair<T, U>> a)
    {
        foreach (KeyValuePair<T, U> p in d) { a(p); }
    }

    public static void ForEach<T, U>(this Dictionary<T, U>.KeyCollection k, Action<T> a)
    {
        foreach (T t in k) { a(t); }
    }

    public static void ForEach<T, U>(this Dictionary<T, U>.ValueCollection v, Action<U> a)
    {
        foreach (U u in v) { a(u); }
    }

Dengan cara ini saya dapat menulis kode seperti ini:

myDictionary.ForEach(pair => Console.Write($"key: {pair.Key}, value: {pair.Value}"));
myDictionary.Keys.ForEach(key => Console.Write(key););
myDictionary.Values.ForEach(value => Console.Write(value););
boca
sumber
6

Kadang-kadang jika Anda hanya perlu nilai untuk dihitung, gunakan koleksi nilai kamus:

foreach(var value in dictionary.Values)
{
    // do something with entry.Value only
}

Dilaporkan oleh pos ini yang menyatakan itu adalah metode tercepat: http://alexpinsker.blogspot.hk/2010/02/c-fastest-way-to-iterate-over.html

ender
sumber
+1 untuk menghadirkan kinerja. Memang, beralih ke kamus itu sendiri mencakup beberapa overhead jika semua yang Anda butuhkan adalah nilai. Itu sebagian besar disebabkan oleh menyalin nilai atau referensi ke struct KeyValuePair.
Jaanus Varus
1
FYI tes terakhir di tautan itu adalah menguji hal yang salah: mengukur iterasi tombol, mengabaikan nilai, sedangkan dua tes sebelumnya menggunakan nilai tersebut.
Crescent Fresh
5

Saya menemukan metode ini dalam dokumentasi untuk kelas DictionaryBase di MSDN:

foreach (DictionaryEntry de in myDictionary)
{
     //Do some stuff with de.Value or de.Key
}

Ini adalah satu-satunya yang saya bisa berfungsi dengan benar di kelas yang diwarisi dari DictionaryBase.


sumber
3
Ini terlihat seperti ketika menggunakan versi non-generik Kamus ... yaitu sebelum .NET framework 2.0.
joedotnot
@ joed0tnot: ini adalah versi non-generik yang digunakan untuk Hashtableobjek
sɐunıɔ ןɐ qɐp
5

foreachtercepat dan jika Anda hanya beralih ___.Values, itu juga lebih cepat

masukkan deskripsi gambar di sini

Pixel_95
sumber
1
Mengapa Anda menelepon ContainsKey()dalam forversi? Itu menambah overhead tambahan yang tidak ada dalam kode yang Anda bandingkan. TryGetValue()ada untuk mengganti pola "jika kunci ada, dapatkan item dengan kunci" yang tepat. Lebih lanjut, jika dictberisi rentang bilangan bulat yang berdekatan dari 0hingga dictCount - 1, Anda tahu pengindeks tidak dapat gagal; jika tidak, dict.Keysadalah apa yang Anda harus iterasi. Either way, tidak ContainsKey()/ TryGetValue()dibutuhkan. Terakhir, tolong jangan memposting tangkapan layar kode.
BACON
3

Saya akan memanfaatkan .NET 4.0+ dan memberikan jawaban yang diperbarui untuk yang diterima semula:

foreach(var entry in MyDic)
{
    // do something with entry.Value or entry.Key
}
yazanpro
sumber
3

Cara standar untuk beralih ke Kamus, menurut dokumentasi resmi pada MSDN adalah:

foreach (DictionaryEntry entry in myDictionary)
{
     //Read entry.Key and entry.Value here
}
Nick
sumber
2

Saya menulis ekstensi untuk mengulang kamus.

public static class DictionaryExtension
{
    public static void ForEach<T1, T2>(this Dictionary<T1, T2> dictionary, Action<T1, T2> action) {
        foreach(KeyValuePair<T1, T2> keyValue in dictionary) {
            action(keyValue.Key, keyValue.Value);
        }
    }
}

Maka Anda bisa menelepon

myDictionary.ForEach((x,y) => Console.WriteLine(x + " - " + y));
Steven Delrue
sumber
Anda mendefinisikan ForEachmetode di mana Anda memiliki foreach (...) { }... Sepertinya tidak perlu.
Maarten
1

Jika mengatakan, Anda ingin mengulangi pengumpulan nilai secara default, saya yakin Anda dapat mengimplementasikan IEnumerable <>, Di mana T adalah jenis objek nilai dalam kamus, dan "ini" adalah Kamus.

public new IEnumerator<T> GetEnumerator()
{
   return this.Values.GetEnumerator();
}

sumber
1

Sebagaimana telah ditunjukkan pada jawaban ini , KeyValuePair<TKey, TValue>mengimplementasikan aDeconstruct metode yang dimulai pada .NET Core 2.0, .NET Standard 2.1 dan .NET Framework 5.0 (pratinjau).

Dengan ini, dimungkinkan untuk beralih melalui kamus dengan KeyValuePaircara agnostik:

var dictionary = new Dictionary<int, string>();

// ...

foreach (var (key, value) in dictionary)
{
    // ...
}
rucamzu
sumber
0
var dictionary = new Dictionary<string, int>
{
    { "Key", 12 }
};

var aggregateObjectCollection = dictionary.Select(
    entry => new AggregateObject(entry.Key, entry.Value));
Pixar
sumber
2
Perlu ada lebih banyak pembenaran / deskripsi dalam jawaban ini. Apa yang AggregateObjectditambahkan KeyValuePair? Di mana "iterasi", seperti yang diminta dalam pertanyaan?
Marc L.
Pilih iterates over the dictionary dan izinkan kami bekerja pada setiap objek. Ini tidak umum foreach, tetapi saya sudah sering menggunakannya. Apakah jawaban saya benar-benar layak untuk diturunkan?
Pixar
Tidak, Select gunakan iterasi untuk mempengaruhi hasil, tetapi bukan iterator itu sendiri. Jenis hal yang foreachdigunakan untuk iterasi ( ) - terutama operasi dengan efek samping - berada di luar ruang lingkup Linq, termasuk Select. Lambda tidak akan berjalan sampai aggregateObjectCollectionbenar-benar disebutkan. Jika jawaban ini diambil sebagai "jalan pertama" (yaitu, digunakan sebelum garis lurus foreach) itu mendorong praktik buruk. Secara situasional, mungkin ada operasi-operasi Linq yang membantu sebelum mengulang kamus, tetapi itu tidak menjawab pertanyaan sebagaimana ditanyakan.
Marc L.
0

Hanya ingin menambahkan 2 sen saya, karena sebagian besar jawaban berkaitan dengan foreach-loop. Silakan lihat kode berikut:

Dictionary<String, Double> myProductPrices = new Dictionary<String, Double>();

//Add some entries to the dictionary

myProductPrices.ToList().ForEach(kvP => 
{
    kvP.Value *= 1.15;
    Console.Writeline(String.Format("Product '{0}' has a new price: {1} $", kvp.Key, kvP.Value));
});

Kalau dipikir-pikir ini menambahkan panggilan tambahan '.ToList ()', mungkin ada sedikit peningkatan kinerja (seperti yang ditunjukkan di sini foreach vs someList.Foreach () {} ), terutama ketika bekerja dengan Kamus besar dan berjalan secara paralel tidak ada option / tidak akan berpengaruh sama sekali.

Juga, harap dicatat bahwa Anda tidak dapat menetapkan nilai ke properti 'Nilai' di dalam foreach-loop. Di sisi lain, Anda akan dapat memanipulasi 'Kunci' juga, mungkin membuat Anda mendapat masalah saat runtime.

Saat Anda hanya ingin "membaca" Tombol dan Nilai, Anda mungkin juga menggunakan IEnumerable.Select ().

var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );
Alex
sumber
5
Menyalin seluruh koleksi tanpa alasan sama sekali tidak akan meningkatkan kinerja. Ini akan secara dramatis memperlambat kode, serta menggandakan jejak memori dari kode yang seharusnya mengkonsumsi hampir tidak ada memori tambahan.
Servy
Saya menghindari metode 'List.ForEach' foreachefek samping : memaksa visibilitas efek samping ke atas, di tempatnya.
user2864740
0

Kamus <TKey, TValue> Ini adalah kelas koleksi generik dalam c # dan menyimpan data dalam format nilai kunci. Kunci harus unik dan tidak boleh nol sedangkan nilai dapat digandakan dan null. Karena setiap item dalam kamus adalah diperlakukan sebagai struktur KeyValuePair <TKey, TValue> yang mewakili kunci dan nilainya. dan karenanya kita harus mengambil tipe elemen KeyValuePair <TKey, TValue> selama iterasi elemen. Di bawah ini adalah contohnya.

Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1,"One");
dict.Add(2,"Two");
dict.Add(3,"Three");

foreach (KeyValuePair<int, string> item in dict)
{
    Console.WriteLine("Key: {0}, Value: {1}", item.Key, item.Value);
}
Sheo Dayal Singh
sumber
0

Jika Anda ingin menggunakan untuk loop, Anda dapat melakukan ini:

var keyList=new List<string>(dictionary.Keys);
for (int i = 0; i < keyList.Count; i++)
{
   var key= keyList[i];
   var value = dictionary[key];
 }
Seçkin Durgay
sumber
Apa manfaatnya? Ini kode yang lebih panjang daripada foreachloop dan kinerja yang lebih buruk karena new List<string>(dictionary.Keys)akan dictionary.Countberulang kali bahkan sebelum Anda memiliki kesempatan untuk iterate sendiri. Mengesampingkan bahwa meminta "cara terbaik" adalah subyektif, saya tidak melihat bagaimana ini akan memenuhi syarat sebagai "cara terbaik" atau "cara standar" yang dicari oleh pertanyaan itu. Untuk "Jika Anda ingin menggunakan untuk loop ..." Saya akan membalas dengan " Jangan gunakan forloop."
BACON
Jika Anda memiliki koleksi besar dan operasi lambat di muka dan jika koleksi Anda dapat berubah ketika Anda mengulangi, itu melindungi Anda dari kesalahan "koleksi telah berubah" dan solusi ini memiliki kinerja yang lebih baik kemudian menggunakan ElementAt.
Seçkin Durgay
Saya setuju bahwa menghindari pengecualian "Koleksi telah dimodifikasi" adalah salah satu alasan untuk melakukan ini, meskipun kasus khusus tidak dinyatakan dalam pertanyaan dan selalu dapat dilakukan foreach (var pair in dictionary.ToArray()) { }. Namun, saya pikir akan lebih baik untuk menjelaskan dalam skenario tertentu (s) di mana seseorang ingin menggunakan kode ini dan implikasi melakukannya.
BACON
ya Anda benar tetapi ada jawaban di sini dengan ElementAt dan memiliki reputasi yang sangat tinggi dan saya memasukkan jawaban ini :)
Seçkin Durgay
-2

sederhana dengan LINQ

 Dictionary<int, string> dict = new Dictionary<int, string>();
 dict.Add(1, "American Ipa");
 dict.Add(2, "Weiss");
 dict.ToList().ForEach(x => Console.WriteLine($"key: {x.Key} | value: {x.Value}") );
Luiz Fernando Corrêa Leite
sumber
Saya melihat bahwa Anda memanggil ToList()karena ForEach()hanya ditentukan pada List<>kelas, tetapi mengapa melakukan semua itu, bukan hanya foreach (var pair in dict) { }? Saya akan mengatakan itu lebih sederhana dan tidak memiliki implikasi memori / kinerja yang sama. Solusi yang tepat ini sudah diusulkan dalam jawaban ini dari 3,5 tahun yang lalu.
BACON
Hanya demi visualisasi kode, itu bisa dilakukan seperti yang Anda indikasikan, saya hanya ingin menyajikannya dengan cara yang berbeda dan dalam pandangan saya yang lebih ramping, saya minta maaf karena tidak melihat jawaban yang ditunjukkan, pelukan
Luiz Fernando Corrêa Leite
-4

selain posting berperingkat tertinggi di mana ada diskusi antara menggunakan

foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

atau

foreach(var entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

paling lengkap adalah yang berikut karena Anda bisa melihat jenis kamus dari inisialisasi, kvp adalah KeyValuePair

var myDictionary = new Dictionary<string, string>(x);//fill dictionary with x

foreach(var kvp in myDictionary)//iterate over dictionary
{
    // do something with kvp.Value or kvp.Key
}
BigChief
sumber
5
Membuat dan menyalin kamus kedua bukanlah solusi yang valid untuk keterbacaan kode. Bahkan, saya berpendapat itu akan membuat kode lebih sulit untuk dipahami karena sekarang Anda harus bertanya pada diri sendiri: "Mengapa orang terakhir membuat kamus kedua?" Jika Anda ingin lebih verbose, cukup gunakan opsi satu.
Matthew Goulart
Saya hanya ingin menunjukkan kepada Anda bahwa ketika menyatakan dict sebelumnya untuk masing-masing, penggunaan di
muka