Jaki jest najlepszy sposób na iterację słownika?


2625

Widziałem kilka różnych sposobów iteracji słownika w języku C #. Czy istnieje standardowy sposób?


108
Dziwię się, że istnieje wiele odpowiedzi na to pytanie, a także jedna pozytywnie oceniona 923 razy .. (używa forach oczywiście) .. Kłócę się, a przynajmniej dodam, że jeśli będziesz musiał iterować słownik, prawdopodobnie jesteś używając go niepoprawnie / niewłaściwie .. Musiałem to skomentować, ponieważ widziałem, jak słowniki są nadużywane w sposób, który IMHO był nieodpowiedni ... Tak, mogą występować rzadkie okoliczności, gdy iterujesz słownik, zamiast wyszukiwania, co jest tym, co jest przeznaczony do… Pamiętaj o tym, zanim zaczniesz zastanawiać się nad iteracją słownika.
Vikas Gupta

74
@VikasGupta Co sugerujesz, aby zrobić coś z kolekcją par klucz-wartość, gdy nie wiesz, jakie będą klucze?
nasch

26
@displayName Jeśli chcesz coś zrobić z każdą parą klucz-wartość, ale nie masz odniesienia do klawiszy, których można użyć do wyszukiwania wartości, przejdziemy przez słownik, prawda? Właśnie wskazałem, że mogą być chwile, w których chciałbyś to zrobić, pomimo twierdzenia Vikas, że jest to zwykle nieprawidłowe użycie.
nasch

34
Stwierdzenie, że jest to nieprawidłowe użycie oznacza, że ​​istnieje lepsza alternatywa. Co to za alternatywa?
Kyle Delaney,

40
VikasGupta się myli, mogę potwierdzić, że po wielu latach wysokowydajnego programowania w języku C # i C ++ w nieoretycznych scenariuszach. Rzeczywiście często zdarza się, że tworzy się słownik, przechowuje unikalne pary klucz-wartość, a następnie iteruje te wartości, o których udowodniono, że mają unikatowe klucze w kolekcji. Tworzenie kolejnych kolekcji jest naprawdę nieefektywnym i kosztownym sposobem uniknięcia iteracji słownika. Podaj dobrą alternatywę jako odpowiedź na pytanie wyjaśniające twój punkt widzenia, w przeciwnym razie twój komentarz jest całkiem bezsensowny.
sɐunıɔ ןɐ qɐp

Odpowiedzi:


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

29
Co jeśli nie znam dokładnie typu klucza / wartości w Słowniku. var entryW takim przypadku użycie jest lepsze, dlatego głosowałem na tę odpowiedź po raz drugi zamiast powyższego.
Ozair Kafray

93
Używanie @OzairKafray, vargdy nie wiesz, że typ jest ogólnie złą praktyką.
Nate

52
Ta odpowiedź jest lepsza, ponieważ Pablo nie użył domyślnie leniwego użycia „var” kodera, które zaciemnia typ zwracany.
MonkeyWrench

119
@MonkeyWrench: Meh. Visual Studio wie, jaki jest typ; wszystko, co musisz zrobić, to najechać kursorem na zmienną, aby się dowiedzieć.
Robert Harvey,

31
Jak rozumiem, vardziała tylko wtedy, gdy typ jest znany w czasie kompilacji. Jeśli program Visual Studio zna ten typ, możesz go również dowiedzieć.
Kyle Delaney,

866

Jeśli próbujesz użyć ogólnego słownika w języku C #, tak jakbyś użył tablicy asocjacyjnej w innym języku:

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

Lub, jeśli potrzebujesz tylko iteracji po kolekcji kluczy, użyj

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

I na koniec, jeśli interesują Cię tylko wartości:

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

(Pamiętaj, że varsłowo kluczowe jest opcjonalną funkcją C # 3.0 i nowszą, tutaj możesz również użyć dokładnego rodzaju swoich kluczy / wartości)


11
funkcja var jest najbardziej wymagana dla pierwszego bloku kodu :)
nawfal

27
Rozumiem, że ta odpowiedź wskazuje, że można iterować po kluczach lub wartościach.
Rotsiser Mho

11
Nie podoba mi się tutaj używanie var. Biorąc pod uwagę, że to tylko cukier syntaktyczny, dlaczego warto go tutaj stosować? Gdy ktoś próbuje odczytać kod, będzie musiał przeskoczyć kod, aby określić typ myDictionary(chyba że jest to oczywiście nazwa). Myślę, że używanie var jest dobre, gdy typ jest oczywisty, np. var x = "some string"Ale gdy nie jest to od razu oczywiste, myślę, że to leniwe kodowanie, które boli czytnika / recenzenta kodu
James Wierzba

8
varmoim zdaniem powinien być stosowany oszczędnie. Szczególnie w tym przypadku nie jest konstruktywny: typ KeyValuePairjest prawdopodobnie istotny dla pytania.
Sinjai,

3
varma wyjątkowy cel i nie sądzę, że jest to cukier „syntaktyczny”. Używanie go celowo jest właściwym podejściem.
Joshua K

159

W niektórych przypadkach może być potrzebny licznik, który może być zapewniony przez implementację pętli For. W tym celu LINQ zapewnia, ElementAtco umożliwia:

for (int index = 0; index < dictionary.Count; index++) {
  var item = dictionary.ElementAt(index);
  var itemKey = item.Key;
  var itemValue = item.Value;
}

21
Aby użyć metody „.ElementAt”, pamiętaj: using System.Linq; To nie jest uwzględnione w FX. automatycznie wygenerowane klasy testowe.
Tinia

14
Jest to odpowiedni sposób, jeśli modyfikujesz wartości związane z kluczami. W przeciwnym razie zgłaszany jest wyjątek podczas modyfikowania i używania foreach ().
Mike de Klerk

27
Czy nie ElementAtjest operacja O (n)?
Arturo Torres Sánchez

162
Ta odpowiedź całkowicie nie zasługuje na tak wiele pozytywnych opinii. Słownik nie ma domyślnej kolejności, więc użycie .ElementAtw tym kontekście może prowadzić do subtelnych błędów. O wiele poważniejszy jest punkt Arturo powyżej. Będziesz iterował dictionary.Count + 1czasy słownikowe prowadzące do złożoności O (n ^ 2) dla operacji, która powinna być tylko O ​​(n). Jeśli naprawdę potrzebujesz indeksu (jeśli tak, to prawdopodobnie używasz niewłaściwego typu kolekcji), powinieneś iterować dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})i nie używać .ElementAtwewnątrz pętli.
spędzić

5
Operacja ElementAt - o (n)! Poważnie? To jest przykład tego, jak nie powinieneś tego robić. Tych wielu zwolenników?
Mukesh Adhvaryu

96

Zależy, czy szukasz kluczy, czy wartości ...

Z Dictionary(TKey, TValue)opisu klasy 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);
}

82

Ogólnie rzecz biorąc, pytanie o „najlepszy sposób” bez określonego kontekstu jest jak pytanie o najlepszy kolor ?

Z jednej strony istnieje wiele kolorów i nie ma najlepszego koloru. To zależy od potrzeby, a często także od smaku.

Z drugiej strony istnieje wiele sposobów na iterację w Słowniku w języku C # i nie ma najlepszego sposobu. To zależy od potrzeby, a często także od smaku.

Najprostszy sposób

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

Jeśli potrzebujesz tylko wartości (pozwala to nazwać item, bardziej czytelna niż kvp.Value).

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

Jeśli potrzebujesz określonej kolejności sortowania

Zasadniczo początkujący są zaskoczeni kolejnością wyliczania słownika.

LINQ zapewnia zwięzłą składnię, która pozwala określić kolejność (i wiele innych rzeczy), np .:

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

Ponownie możesz potrzebować tylko tej wartości. LINQ zapewnia również zwięzłe rozwiązanie dla:

  • iteruj bezpośrednio na wartości (pozwala to nazwać item, bardziej czytelną niżkvp.Value )
  • ale posortowane według kluczy

Oto on:

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

Z tych przykładów można zrobić wiele innych rzeczywistych przypadków użycia. Jeśli nie potrzebujesz konkretnego zamówienia, po prostu trzymaj się „najprostszej drogi” (patrz wyżej)!


Ostatni powinien być, .Valuesa nie klauzulą ​​select.
Mafii,

@Mafii Czy jesteś pewien? Wartości zwracane przez OrderBy nie są typu KeyValuePair, nie mają Valuepola. Dokładny typ, który tu widzę, to IOrderedEnumerable<KeyValuePair<TKey, TValue>>. Może miałeś na myśli coś innego? Czy potrafisz napisać pełną linię, która ma na myśli (i przetestować)?
Stéphane Gourichon

Myślę, że ta odpowiedź zawiera to, co mam na myśli: stackoverflow.com/a/141105/5962841, ale poprawcie mnie, jeśli coś
pomylę

1
@Mafii Ponownie przeczytaj całą moją odpowiedź, wyjaśnienia między sekcjami kodu określają kontekst. Odpowiedź, o której wspominasz, jest jak druga sekcja kodu w mojej odpowiedzi (nie wymaga zamówienia). Tam właśnie napisałem, items.Valuejak zasugerowałeś. W przypadku czwartej sekcji, którą skomentowałeś, Select()jest to sposób foreachna wyliczenie bezpośrednio wartości w słowniku zamiast par klucz-wartość. Jeśli jakoś ci się nie podoba Select()w tym przypadku, możesz wybrać trzecią sekcję kodu. Celem czwartej sekcji jest pokazanie, że można wstępnie przetworzyć kolekcję za pomocą LINQ.
Stéphane Gourichon

1
Jeśli to zrobisz .Keys.Orderby(), przejdziesz do listy kluczy. Jeśli to wszystko, czego potrzebujesz, w porządku. Jeśli potrzebujesz wartości, w pętli będziesz musiał sprawdzić słownik każdego klucza, aby uzyskać wartość. W wielu scenariuszach nie będzie to miało praktycznej różnicy. W scenariuszu o wysokiej wydajności tak będzie. Tak jak napisałem na początku odpowiedzi: „istnieje wiele sposobów (...) i nie ma najlepszego sposobu. Zależy to od potrzeby, a często także od smaku”.
Stéphane Gourichon

53

Powiedziałbym, że foreach jest standardowym sposobem, choć oczywiście zależy od tego, czego szukasz

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

Czy tego szukasz?


42
Um, czy nazywanie elementu „wartością” nie jest dość mylące? Zwykle używasz składni, takiej jak „wartość.Klucz” i „wartość.Wartość”, co nie jest zbyt intuicyjne dla każdego, kto będzie czytał kod, zwłaszcza jeśli nie jest zaznajomiony ze sposobem implementacji słownika .Net .
RenniePet

3
@RenniePet kvpjest powszechnie stosowany do nazwy instancji KeyValuePair podczas iteracji nad słownikami i związanych z nimi struktur danych: foreach(var kvp in myDictionary){....
mbx,

37

Możesz także wypróbować to w dużych słownikach do przetwarzania wielowątkowego.

dictionary
.AsParallel()
.ForAll(pair => 
{ 
    // Process pair.Key and pair.Value here
});

8
@WiiMaxx i co ważniejsze, jeśli te elementy NIE są od siebie zależne
Mafii

36

W wersji C # 7.0 wprowadzono dekonstruktory, a jeśli używasz aplikacji .NET Core 2.0+ , strukturaKeyValuePair<>już zawieraDeconstruct()dla ciebie. Możesz więc zrobić:

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}");
}

wprowadź opis zdjęcia tutaj


5
Jeśli używasz .NET Framework, który przynajmniej do wersji 4.7.2 nie ma dekonstrukcji na KeyValuePair, spróbuj tego:foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
nwsmith 26.04.19

29

Rozumiem, że na to pytanie udzielono już wielu odpowiedzi, ale chciałem przeprowadzić trochę badań.

Iterowanie po słowniku może być dość powolne w porównaniu z iteracją po czymś takim jak tablica. W moich testach iteracja na tablicy zajęła 0,015003 sekundy, podczas gdy iteracja na słowniku (z taką samą liczbą elementów) zajęła 0,0365073 sekundy, czyli 2,4 razy dłużej! Chociaż widziałem znacznie większe różnice. Dla porównania lista była gdzieś pomiędzy 0,00215043 sekund.

To jednak jak porównywanie jabłek i pomarańczy. Chodzi mi o to, że iteracja po słownikach jest powolna.

Słowniki są zoptymalizowane do wyszukiwania, więc mając to na uwadze, stworzyłem dwie metody. Jeden po prostu robi foreach, drugi iteruje klucze, a potem podnosi wzrok.

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

    return "Normal";
}

Ten ładuje klucze i zamiast tego iteruje je (próbowałem też wciągnąć klucze do łańcucha [], ale różnica była znikoma.

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";
}

W tym przykładzie normalny test foreach trwał 0,0310062, a wersja kluczy - 0,2205441. Ładowanie wszystkich kluczy i powtarzanie wszystkich wyszukiwań jest zdecydowanie DUŻO wolniejsze!

W ostatnim teście wykonałem iterację dziesięć razy, aby sprawdzić, czy korzystanie z klawiszy jest w tym przypadku korzystne (w tym momencie byłem po prostu ciekawy):

Oto metoda RunTest, jeśli pomaga to zobrazować, co się dzieje.

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);
}

Tutaj normalny przebieg Foreach trwał 0,2820564 sekund (około dziesięć razy dłużej niż pojedyncza iteracja - jak można się spodziewać). Iteracja klawiszy zajęła 2.2249449 sekund.

Edytowane w celu dodania: przeczytanie niektórych innych odpowiedzi skłoniło mnie do pytania, co by się stało, gdybym użył Słownika zamiast Słownika. W tym przykładzie tablica zajęła 0,0120024 sekundy, lista 0,0185037 sekund i słownik 0,0465093 sekundy. Można oczekiwać, że typ danych ma wpływ na to, o ile wolniej działa słownik.

Jakie są moje wnioski ?

  • Jeśli to możliwe, unikaj iteracji słownika, są one znacznie wolniejsze niż iteracja tablicy z tymi samymi danymi.
  • Jeśli zdecydujesz się na iterację w słowniku, nie próbuj być zbyt sprytny, chociaż wolniej możesz zrobić o wiele gorzej niż przy użyciu standardowej metody foreach.

11
Powinieneś mierzyć za pomocą czegoś takiego jak StopWatch zamiast DateTime: hanselman.com/blog/…
Nawet Mien

2
czy mógłbyś opisać swój scenariusz testowy, ile pozycji w słowniku, jak często uruchamiałeś scenariusz, aby obliczyć średni czas, ...
WiiMaxx

3
Co ciekawe, otrzymasz różne wyniki w zależności od tego, jakie dane masz w słowniku. Podczas iteracji po słowniku funkcja modułu wyliczającego musi pomijać wiele pustych miejsc w słowniku, co powoduje, że jest wolniejsza niż iteracja po tablicy. Jeśli słownik jest pełny, mniej wolnych miejsc do pominięcia będzie mniej niż w połowie pustych.
Martin Brown

26

Istnieje wiele opcji. Moim osobistym faworytem jest KeyValuePair

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

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

Możesz także użyć kolekcji kluczy i wartości


14

Z .NET Framework 4.7jednym można użyć rozkładu

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

Aby ten kod działał w niższych wersjach C #, dodaj System.ValueTuple NuGet packagei napisz gdzieś

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;
    }
}

9
To jest niepoprawne. .NET 4.7 po prostu ma ValueTuplewbudowane. Jest dostępny jako pakiet nuget dla wcześniejszych wersji. Co ważniejsze, C # 7.0+ jest potrzebny, aby Deconstructmetoda działała jako dekoder var (fruit, number) in fruits.
David Arno,

13

Począwszy od C # 7, można dekonstruować obiekty na zmienne. Uważam, że jest to najlepszy sposób na iterację słownika.

Przykład:

Utwórz metodę rozszerzenia, KeyValuePair<TKey, TVal>która go dekonstruuje:

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

Powtarzaj dowolne Dictionary<TKey, TVal>w następujący sposób

// 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}");
}

10

Sugerujesz poniżej, aby iterować

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

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

Do Twojej wiadomości, foreachnie działa, jeśli wartość jest typu obiekt.


4
Proszę rozwinąć: foreachnie będzie działać, jeśli która wartość jest typu object? W przeciwnym razie nie ma to większego sensu.
Marc L.,

9

Najprostsza forma do iteracji słownika:

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

9

Korzystając z C # 7 , dodaj tę metodę rozszerzenia do dowolnego projektu swojego rozwiązania:

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);
    }
}


I użyj tej prostej składni

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


Lub ten, jeśli wolisz

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


W miejsce tradycyjnego

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

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


Metoda rozszerzenia przekształca KeyValuePairtwoją IDictionary<TKey, TValue>w silnie typowanątuple , umożliwiając ci użycie tej nowej wygodnej składni.

Konwertuje -just- wymagane pozycje słownika na tuples, więc NIE konwertuje całego słownika natuples , więc nie ma z tym żadnych problemów związanych z wydajnością.

Istnieje tylko niewielki koszt wywołania metody rozszerzenia dla utworzenia tuplew porównaniu z KeyValuePairbezpośrednim użyciem , co NIE powinno stanowić problemu, jeśli przypisujesz KeyValuePairwłaściwości KeyiValue nowe zmienne pętli.

W praktyce ta nowa składnia bardzo dobrze pasuje do większości przypadków, z wyjątkiem scenariuszy o bardzo wysokiej wydajności na niskim poziomie, w których nadal możesz po prostu nie używać jej w tym konkretnym miejscu.

Sprawdź to: Blog MSDN - Nowe funkcje w C # 7


1
Jaki byłby powód, aby preferować krotki „wygodne” niż pary klucz-wartość? Nie widzę tu zysku. Twoja krotka zawiera klucz i wartość, podobnie jak para klucz-wartość.
Maarten,

4
Cześć Maarten, dziękuję za twoje pytanie. Główną korzyścią jest czytelność kodu bez dodatkowego wysiłku programistycznego. Z KeyValuePair należy zawsze używać formularza kvp.Keyi kvp.Valuedo używania odpowiednio klucza i wartości. Dzięki krotkom możesz dowolnie nazywać klucz i wartość, bez potrzeby używania dalszych deklaracji zmiennych w bloku foreach. Na przykład możesz nazwać swój klucz jako factoryName, a wartość jako models, co jest szczególnie przydatne, gdy otrzymujesz zagnieżdżone pętle (słowniki słowników): obsługa kodu staje się znacznie łatwiejsza. Po prostu daj temu szansę! ;-)
sɐunıɔ ןɐ qɐp

7

Wiem, że to bardzo stare pytanie, ale stworzyłem kilka metod rozszerzenia, które mogą być przydatne:

    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); }
    }

W ten sposób mogę napisać taki kod:

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

6

Czasami, jeśli potrzebujesz tylko wyliczyć wartości, skorzystaj z kolekcji wartości słownika:

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

Zgłoszone przez ten post, który stwierdza, że ​​jest to najszybsza metoda: http://alexpinsker.blogspot.hk/2010/02/c-fastest-way-to-iterate-over.html


+1 za zwiększenie wydajności. Rzeczywiście, iteracja po samym słowniku zawiera pewien narzut, jeśli wszystko, czego potrzebujesz, to wartości. Wynika to głównie z kopiowania wartości lub referencji do struktur KeyValuePair.
Jaanus Varus

1
FYI ostatnim testem w tym łączu jest testowanie niewłaściwej rzeczy: mierzy iterację po klawiszach, ignorując wartości, podczas gdy dwa poprzednie testy używają tej wartości.
Crescent Fresh

5

Znalazłem tę metodę w dokumentacji klasy DictionaryBase na MSDN:

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

To był jedyny, w którym mogłem poprawnie funkcjonować w klasie odziedziczonej ze DictionaryBase.


3
Wygląda to tak, jak w przypadku korzystania z nie-ogólnej wersji Dictionary ... tj. Przed .NET Framework 2.0.
joedotnot

@ joed0tnot: jest to wersja Hashtable
ogólna

5

foreachjest najszybszy, a jeśli tylko wykonasz iterację ___.Values, będzie także szybszy

wprowadź opis zdjęcia tutaj


Dlaczego dzwonisz ContainsKey()w forwersji? To dodaje dodatkowy narzut, którego nie ma w kodzie, z którym porównujesz. TryGetValue()istnieje, aby zastąpić dokładny wzór „jeśli klucz istnieje, zdobądź przedmiot z kluczem”. Ponadto, jeśli dictzawiera ciągły zakres liczb całkowitych od 0do dictCount - 1, wiesz, że indeksator nie może zawieść; w przeciwnym dict.Keysrazie powinieneś powtarzać. Tak czy inaczej, nie ContainsKey()/ TryGetValue()potrzebne. Na koniec nie publikuj zrzutów ekranu kodu.
BACON

3

Wykorzystam .NET 4.0+ i przedstawię zaktualizowaną odpowiedź na pierwotnie zaakceptowaną:

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

3

Standardowy sposób iteracji w Słowniku, zgodnie z oficjalną dokumentacją MSDN, to:

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

2

Napisałem rozszerzenie do pętli w słowniku.

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);
        }
    }
}

Następnie możesz zadzwonić

myDictionary.ForEach((x,y) => Console.WriteLine(x + " - " + y));

Zdefiniowałeś ForEachmetodę, w której masz foreach (...) { }... Wydaje się niepotrzebny.
Maarten

1

Jeśli powiedzmy, że chcesz domyślnie iterować kolekcję wartości, uważam, że możesz zaimplementować IEnumerable <>, gdzie T to typ obiektu wartości w słowniku, a „this” to Dictionary.

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

1

Chciałem tylko dodać moje 2 centy, ponieważ większość odpowiedzi dotyczy pętli foreach. Proszę spojrzeć na następujący kod:

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));
});

Mimo że dodaje to dodatkowe wywołanie funkcji „.ToList ()”, może wystąpić nieznaczna poprawa wydajności (jak wskazano tutaj foreach vs someList.Foreach () {} ), szczególnie w przypadku pracy z dużymi słownikami i równoległego działania nie opcja / nie przyniesie żadnego efektu.

Należy również pamiętać, że nie będzie można przypisać wartości do właściwości „Wartość” w pętli foreach. Z drugiej strony będziesz mógł również manipulować „kluczem”, co może spowodować kłopoty w czasie wykonywania.

Gdy chcesz po prostu „odczytać” klucze i wartości, możesz także użyć IEnumerable.Select ().

var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );

5
Kopiowanie całej kolekcji bez żadnego powodu nie poprawi wydajności. Spowoduje to radykalne spowolnienie kodu, a także podwojenie śladu pamięci kodu, który nie powinien zajmować praktycznie żadnej dodatkowej pamięci.
Servy

Unikam ubocznej metody „List.ForEach”: foreachwymusza widoczność efektu ubocznego tam, gdzie należy.
user2864740,

0
var dictionary = new Dictionary<string, int>
{
    { "Key", 12 }
};

var aggregateObjectCollection = dictionary.Select(
    entry => new AggregateObject(entry.Key, entry.Value));

2
W tej odpowiedzi należy podać więcej uzasadnienia / opisu. Co to AggregateObjectdodaje KeyValuePair? Gdzie jest „iteracja”, zgodnie z pytaniem w pytaniu?
Marc L.,

Wybierz iteracje nad słownikiem i pozwala nam pracować nad każdym obiektem. Nie jest tak ogólny jak foreach, ale często go używałem. Czy moja odpowiedź naprawdę zasługiwała na głos?
Pixar

Nie, Select używa iteracji do uzyskania wyniku, ale nie jest samą iteratorem. Rodzaje rzeczy, do których foreachużywana jest iteracja ( ) - zwłaszcza operacje z efektami ubocznymi - nie wchodzą w zakres Linq, w tym Select. Lambda nie uruchomi się, dopóki nie aggregateObjectCollectionzostanie wyliczona. Jeśli ta odpowiedź jest traktowana jako „pierwsza ścieżka” (tj. Stosowana przed stritem foreach), zachęca do złych praktyk. W niektórych sytuacjach operacje Linq mogą być pomocne przed iteracją słownika, ale nie rozwiązują zadanego pytania.
Marc L.

0

Słownik <TKey, TValue> Jest to ogólna klasa kolekcji w języku c # i przechowuje dane w formacie wartości klucza. Klucz musi być unikalny i nie może mieć wartości zerowej, natomiast wartość może być zduplikowana i pusta. Każda pozycja w słowniku jest traktowane jako struktura KeyValuePair <TKey, TValue> reprezentująca klucz i jego wartość. i dlatego powinniśmy wziąć typ elementu KeyValuePair <TKey, TValue> podczas iteracji elementu. Poniżej znajduje się przykład.

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);
}

0

Jeśli chcesz użyć pętli, możesz to zrobić:

var keyList=new List<string>(dictionary.Keys);
for (int i = 0; i < keyList.Count; i++)
{
   var key= keyList[i];
   var value = dictionary[key];
 }

Jaka jest z tego korzyść? Jest dłuższy kod niż foreachpętla i gorsza wydajność, ponieważ new List<string>(dictionary.Keys)będą iterować dictionary.Countczasy, zanim jeszcze będziesz mieć szansę na iterację. Pomijając fakt, że pytanie o „najlepszy sposób” jest subiektywne, nie rozumiem, jak można by to zakwalifikować jako „najlepszy sposób” lub „standardowy sposób”, którego szuka pytanie. Do „Jeśli chcesz użyć pętli ...” odpowiedziałbym „ Nie używaj forpętli”.
BACON

Jeśli masz dużą kolekcję, a operacje są zbyt wolne, a twoja kolekcja może się zmieniać podczas iteracji, chroni cię przed błędem „kolekcja została zmieniona” i to rozwiązanie ma lepszą wydajność niż użycie ElementAt.
Seçkin Durgay

Zgadzam się, że unikanie wyjątków „Kolekcja została zmodyfikowana” jest jednym z powodów, aby to zrobić, chociaż ten szczególny przypadek nie został podany w pytaniu i zawsze można to zrobić foreach (var pair in dictionary.ToArray()) { }. Mimo to myślę, że dobrze byłoby wyjaśnić w odpowiedzi konkretny scenariusz (scenariusze), w których chciałoby się użyć tego kodu oraz konsekwencje takiego postępowania.
BACON

tak, masz rację, ale tutaj jest element z ElementAt i ma bardzo wysoką reputację i
wpisałem

-1

proste z 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}") );

Widzę, że dzwonisz, ToList()ponieważ ForEach()jest zdefiniowany tylko w List<>klasie, ale dlaczego to wszystko zamiast po prostu foreach (var pair in dict) { }? Powiedziałbym, że jest to jeszcze prostsze i nie ma takich samych implikacji pamięci / wydajności. W każdym razie dokładnie to rozwiązanie zostało już zaproponowane w tej odpowiedzi sprzed 3,5 roku.
BACON

Tylko ze względu na wizualizację kodu, można to naprawdę zrobić tak, jak wskazałeś, chciałem tylko przedstawić go w inny sposób i moim szczuplejszym widokiem przepraszam, że nie zobaczyłem wskazanej odpowiedzi, uścisk
Luiz Fernando Corrêa Leite

-3

oprócz postów o najwyższym rankingu, w których istnieje dyskusja między używaniem

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

lub

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

najbardziej kompletny jest następujący, ponieważ można zobaczyć typ słownika z inicjalizacji, kvp to 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
}

5
Tworzenie i kopiowanie drugiego słownika nie jest prawidłowym rozwiązaniem dla czytelności kodu. W rzeczywistości twierdziłbym, że to utrudni zrozumienie kodu, ponieważ teraz musisz zadać sobie pytanie: „Dlaczego ostatni facet stworzył drugi słownik?” Jeśli chcesz być bardziej szczegółowy, skorzystaj z opcji pierwszej.
Matthew Goulart,

Chciałem wam tylko pokazać, że kiedy ogłaszam wcześniej dyktowanie dla każdego, użycie w foreach jest jasne z deklaracji
BigChief
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.