Wat is de beste manier om te itereren over een woordenboek?

Ik heb een paar verschillende manieren gezien om over een woordenboek in C # te herhalen. Is er een standaard manier?


1, Autoriteit 100%

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

2, Autoriteit 23%

Als u probeert een generiek woordenboek in C # zoals u een associatieve array in een andere taal te gebruiken:

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

of, als u alleen maar over de verzameling sleutels nodig hebt,

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

en ten slotte, als u alleen geïnteresseerd bent in de waarden:

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

(Let op: de vartrefwoord is een optionele C # 3.0 en bovenstaande functie, u kunt hier ook het exacte type van uw sleutels / waarden gebruiken)


3, Autoriteit 4%

In sommige gevallen heeft u mogelijk een teller nodig die kan worden verstrekt door de implementatie van de lus. Daarvoor biedt LINQ ElementAtwaarmee het volgende mogelijk is:

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

4, Autoriteit 3%

Afhankelijk van of je op zoek bent naar de sleutels of de waarden…

Van de MSDN Dictionary(TKey, TValue)Klasbeschrijving:

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

Antwoord 5, autoriteit 2%

Over het algemeen is vragen naar ‘de beste manier’ zonder een specifieke context hetzelfde als vragen
wat is de beste kleur?

Enerzijds zijn er veel kleuren en is er geen beste kleur. Het hangt af van de behoefte en vaak ook van de smaak.

Aan de andere kant zijn er veel manieren om een Dictionary in C# te herhalen en er is geen beste manier. Het hangt af van de behoefte en vaak ook van de smaak.

Meest eenvoudige manier

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

Als je alleen de waarde nodig hebt (maakt het mogelijk om het itemte noemen, leesbaarder dan kvp.Value).

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

Als je een specifieke sorteervolgorde nodig hebt

In het algemeen zijn beginners verbaasd over de volgorde van opsomming van een woordenboek.

LINQ biedt een beknopte syntaxis waarmee de volgorde (en vele andere dingen) kan worden gespecificeerd, bijvoorbeeld:

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

Ook hier heb je misschien alleen de waarde nodig. LINQ biedt ook een beknopte oplossing voor:

  • rechtstreeks op de waarde herhalen (maakt het mogelijk om het itemte noemen, leesbaarder dan kvp.Value)
  • maar gesorteerd op de toetsen

Hier is het:

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

Er zijn nog veel meer praktijkvoorbeelden die u kunt gebruiken op basis van deze voorbeelden.
Als je geen specifieke bestelling nodig hebt, blijf dan gewoon op de “meest eenvoudige manier” (zie hierboven)!


Antwoord 6

Ik zou zeggen dat foreach de standaardmanier is, hoewel het natuurlijk afhangt van wat je zoekt

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

Is dat wat u zoekt?


Antwoord 7

C # 7.0 geïntroduceerd Deconstructors en als u .NET Core 2.0 + Applicatie, de structuur KeyValuePair<>bevatten reeds een Deconstruct()voor u. Dus je kunt doen:

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


8

U kunt ook dit proberen op grote woordenboeken voor multithreaded verwerking.

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

9

Ik stel deze vraag heeft al veel reacties gehad, maar ik wilde gooien in een beetje onderzoek.

itereren over een woordenboek kan nogal traag zijn in vergelijking met het itereren over iets als een array. In mijn tests een iteratie over een array nam 0.015003 seconden terwijl een iteratie over een woordenboek (met hetzelfde aantal elementen) nam 0,0365073 seconden dat is 2,4 keer zo lang! Hoewel ik veel grotere verschillen hebben gezien. Ter vergelijking was een lijst ergens tussen in 0.00215043 seconden.

Maar dat is als appels met peren vergelijken. Mijn punt is dat de iteratie dan woordenboeken is traag.

Woordenboeken zijn geoptimaliseerd voor zoekopdrachten, dus met dat in gedachten heb ik twee methoden gemaakt. De een doet gewoon een foreach, de ander herhaalt de toetsen en kijkt dan op.

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

Deze laadt de toetsen en herhaalt ze in plaats daarvan (ik heb ook geprobeerd de toetsen in een string te trekken[] maar het verschil was te verwaarlozen.

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

Bij dit voorbeeld kostte de normale foreach-test 0,0310062 en de sleutelversie 0,2205441. Het laden van alle sleutels en het herhalen van alle zoekopdrachten is duidelijk VEEL langzamer!

Voor een laatste test heb ik mijn iteratie tien keer uitgevoerd om te zien of er voordelen zijn aan het gebruik van de toetsen hier (op dit punt was ik gewoon nieuwsgierig):

Hier is de RunTest-methode als dat je helpt te visualiseren wat er aan de hand is.

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

Hier duurde de normale foreach-run 0,2820564 seconden (ongeveer tien keer langer dan een enkele iteratie, zoals je zou verwachten). De iteratie over de toetsen duurde 2.2249449 seconden.

Bewerkt om toe te voegen:
Toen ik enkele van de andere antwoorden las, vroeg ik me af wat er zou gebeuren als ik Dictionary zou gebruiken in plaats van Dictionary. In dit voorbeeld duurde de array 0,0120024 seconden, de lijst 0,0185037 seconden en de woordenlijst 0,0465093 seconden. Het is redelijk om te verwachten dat het gegevenstype een verschil maakt in hoeveel langzamer het woordenboek is.

Wat zijn mijn conclusies?

  • Vermijd het herhalen van een woordenboek als je kunt, ze zijn aanzienlijk langzamer dan het herhalen van een array met dezelfde gegevens erin.
  • Als je ervoor kiest om een woordenboek te herhalen, probeer dan niet te slim te zijn, hoewel je langzamer zou kunnen doen dan het gebruik van de standaard foreach-methode.

Antwoord 10

Er zijn tal van opties. Mijn persoonlijke favoriet is van KeyValuePair

Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here
foreach (KeyValuePair<string,object> kvp in myDictionary)
{
     // Do some interesting things
}

U kunt ook de verzamelingen sleutels en waarden gebruiken


Antwoord 11

Zoals reeds aangegeven in dit answer, KeyValuePair<TKey, TValue>implementeert een Deconstruct-methode vanaf .NET Core 2.0, .NET Standard 2.1 en .NET Framework 5.0 (preview).

Hiermee is het mogelijk om te herhalen via een woordenboek in een KeyValuePairagnostische manier:

var dictionary = new Dictionary<int, string>();
// ...
foreach (var (key, value) in dictionary)
{
    // ...
}

12

Met .NET Framework 4.7Men kan Ontbinding

gebruiken

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

Om deze code te laten werken aan lagere C # -versies, voeg System.ValueTuple NuGet packagetoe en schrijf ergens

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

13

Als C # 7 kunt u objecten in variabelen deconstrueren. Ik geloof dat dit de beste manier is om over een woordenboek te herhalen.

Voorbeeld:

Maak een uitbreidingsmethode op KeyValuePair<TKey, TVal>DAT DECONSTUMTS IT:

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

Herven over een Dictionary<TKey, TVal>op de volgende manier

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

Antwoord 14

U heeft hieronder voorgesteld om te herhalen

Dictionary<string,object> myDictionary = new Dictionary<string,object>();
//Populate your dictionary here
foreach (KeyValuePair<string,object> kvp in myDictionary) {
    //Do some interesting things;
}

Ter info, foreachwerkt niet als de waarde van het type object is.


Antwoord 15

Eenvoudigste vorm om een woordenboek te herhalen:

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

Antwoord 16

Gebruik C# 7en voeg deze extensiemethodetoe aan elk project van uw oplossing:

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

En gebruik deze eenvoudige syntaxis

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

Of deze, zo je wilt

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

In plaats van de traditionele

foreach (KeyValuePair<string, object> kvp in dict)
{
    string id = kvp.Key;
    object value = kvp.Value;
    // your code using 'id' and 'value'
}

De extension-methode transformeert de KeyValuePairvan uw IDictionary<TKey, TValue>In een sterk getypt tuple, zodat u deze nieuwe comfortabele syntaxis kunt gebruiken.

Het converteert – de vereiste woordenboekinvoering naar tuples, dus het converteert het hele woordenboek niet naar tuples, dus er zijn geen prestatiebepalen.

Er is slechts een kleine kosten die de uitbreidingsmethode oproepen voor het maken van een tuplein vergelijking met het gebruik van de KeyValuePairrechtstreeks, wat geen probleem zou moeten zijn als u de KeyValuePair‘S EIGENSCHAPPEN KeyEN ValueNAAR NIEUWE LOOPVARIABELEN.

In de praktijk past deze nieuwe syntaxis past heel goed voor de meeste gevallen, behalve voor ultra-high-performance scenario’s met een laag niveau, waar u nog steeds de mogelijkheid hebt om het gewoon niet op die specifieke plek te gebruiken.

Controleer dit: MSDN Blog – Nieuwe functies in C # 7


17

foreachis het snelst en als u alleen over ___.Values, is het ook sneller


18

Ik weet dat dit een heel oude vraag is, maar ik heb enkele uitbreidingsmethoden gemaakt die nuttig kunnen zijn:

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

Op deze manier kan ik code als volgt schrijven:

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

Antwoord 19

Ik vond deze methode in de documentatie voor de klasse DictionaryBase op MSDN:

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

Dit was de enige die ik correct kon laten functioneren in een klas die geërfd was van de DictionaryBase.


Antwoord 20

Soms, als je alleen de waarden nodig hebt om te inventariseren, gebruik dan de waardeverzameling van het woordenboek:

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

Gemeld door dit bericht waarin staat dat dit de snelste methode is:
http://alexpinsker.blogspot.hk/2010 /02/c-fastest-way-to-iterate-over.html


21

Ik heb het voordeel van .NET 4.0+ en geef een bijgewerkt antwoord op de oorspronkelijk geaccepteerde One:

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

22

De standaardmanier om te itereren boven een woordenboek, volgens de officiële documentatie op MSDN is:

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

23

Ik schreef een extensie om over een woordenboek te lussen.

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

Dan kunt u

bellen

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

24

Indien zeggen, u wilt standaard over de waardencollectie herhalen, ik geloof dat u Ienumerable & LT; & GT;, waarbij T het type van het waardenobject in het Woordenboek is, en “dit” is een woordenboek.

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

25

Als u voor lus wilt gebruiken, kunt u dit doen:

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

26

var dictionary = new Dictionary<string, int>
{
    { "Key", 12 }
};
var aggregateObjectCollection = dictionary.Select(
    entry => new AggregateObject(entry.Key, entry.Value));

27

wilde gewoon mijn 2 cent toevoegen, omdat de meeste antwoorden betrekking hebben op foreach-lus.
Bekijk alstublieft de volgende code:

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

Hoewel dit een extra aanroep van ‘.ToList()’ toevoegt, kan er een lichte prestatieverbetering zijn (zoals hier aangegeven foreach vs someList.Foreach(){}),
vooral bij het werken met grote woordenboeken en parallel werken is geen optie / heeft helemaal geen effect.

Houd er ook rekening mee dat u geen waarden kunt toewijzen aan de eigenschap ‘Value’ binnen een foreach-lus. Aan de andere kant kun je de ‘sleutel’ ook manipuleren, waardoor je mogelijk in de problemen komt tijdens runtime.

Als u alleen sleutels en waarden wilt “lezen”, kunt u ook IEnumerable.Select() gebruiken.

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

Antwoord 28

Het beste antwoord is natuurlijk: Gebruik geen exact woordenboek als je van plan bent erover te herhalen– zoals Vikas Gupta al zei in de discussie onder de vraag. Maar die discussie, aangezien deze hele thread nog steeds verrassend goede alternatieven mist. Een daarvan is:

SortedList<string, string> x = new SortedList<string, string>();
x.Add("key1", "value1");
x.Add("key2", "value2");
x["key3"] = "value3";
foreach( KeyValuePair<string, string> kvPair in x )
            Console.WriteLine($"{kvPair.Key}, {kvPair.Value}");

Waarom veel mensen het beschouwen als een codegeur van het herhalen van een woordenboek (bijv. door foreach(KeyValuePair<,>):
Het is nogal verrassend, maar er is een beetje aangehaald, maar basisprincipe van Clean Coding:
Express intentie!
Robert C. Martin schrijft in “Cleane Code”: “Namen kiezen die de intentie onthullen”. Uiteraard is dit te zwak. “Express (onthullen) Intent met elke coderingsbeslissing” zou beter zijn. Mijn formulering. Ik mis een goede eerste bron, maar ik weet zeker dat er …
Een gerelateerd principe is “principe van minstens verrassing ” (= Principe van minst verbaasd ).

Waarom is dit gerelateerd aan het herhalen van een woordenboek? Een woordenboek kiezen, drukt de intentie uit om een ​​gegevensstructuur te kiezen die is gemaakt voor het vinden van gegevens per sleutel . Tegenwoordig zijn er zoveel alternatieven in .NET, als u wilt herhalen via sleutel / waardeparen die u niet nodig hebt deze kruk.

Bovendien: als u over iets herhaalt, moet u iets onthullen over hoe de items (te zijn) bestellen en naar verwachting worden besteld!
AFAIK, Dictionary heeft geen specificatie over het bestellen (alleen implementatie-specifieke conventies).
Wat zijn de alternatieven?

TLDR:
Sorteerlijst : Als uw verzameling niet te groot wordt, zou een eenvoudige oplossing zijn om SortedList & LT;, & GT; wat u ook volledige indexering van sleutel / waardeparen geeft.

Microsoft heeft een lang artikel over het vermelden en uitleggen van passende collecties:
Slaged Collection

Om het belangrijkste te vermelden: KeyedCollection & LT;, & GT; en SortedDictionary & LT;, & GT; .
SortedDictionary & LT;, & GT; Is een beetje sneller dan sorteerlijst voor Oly-invoeging als het groot wordt, maar mist indexering en is alleen nodig als O (log n) voor het invoegen wordt bijgevoegd over andere bewerkingen. Als je echt o (1) nodig hebt voor het invoegen en accepteren van langzamere iteratie in ruil, moet je blijven bij Simple Dictionary & LT;, & GT;.
Uiteraard is er geen gegevensstructuur die het snelst is voor elke mogelijke operatie.

Bovendien is er ImmutablesSortedDICTIONARIAIRE & LT;, & GT;

En als een gegevensstructuur niet precies is wat u nodig heeft, dan afgeleid van woordenboek & lt;, & GT; of zelfs van het nieuwe concurrentdictionary & lt;, & GT; en voeg expliciete iteratie / sorteerfuncties toe!


29

Naast de hoogste rangs-posten waar een discussie is tussen het gebruik van

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

of

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

Het meest compleet is het volgende omdat u het woordenboektype van de initialisatie kunt zien, KVP is 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
}

Other episodes