Quelle est la meilleure façon de parcourir un dictionnaire?


2628

J'ai vu plusieurs façons différentes d'itérer sur un dictionnaire en C #. Existe-t-il une méthode standard?


108
Je suis surpris qu'il y ait beaucoup de réponses à cette question, ainsi qu'une sur-votée 923 fois .. (utilise pour chaque cours) .. Je dirais, ou du moins ajouter que si vous devez parcourir un dictionnaire, les chances sont que vous êtes l'utiliser de manière incorrecte / inappropriée .. J'ai dû faire ce commentaire, car j'ai vu des dictionnaires abusés d'une manière qui à mon humble avis n'était pas appropriée ... Oui, il peut y avoir de rares circonstances lorsque vous parcourez le dictionnaire, plutôt que de rechercher, il est conçu pour .. Veuillez garder cela à l'esprit, avant de vous demander comment parcourir un dictionnaire.
Vikas Gupta

74
@VikasGupta Que suggéreriez-vous pour faire quelque chose avec une collection de paires clé-valeur lorsque vous ne savez pas quelles seront les clés?
nasch

26
@displayName Si vous voulez faire quelque chose avec chaque paire clé-valeur mais que vous n'avez pas de référence aux clés à utiliser pour rechercher des valeurs, vous devez parcourir le dictionnaire, non? Je soulignais simplement qu'il pourrait y avoir des moments où vous voudriez faire cela, malgré l'affirmation de Vikas selon laquelle il s'agit généralement d'une utilisation incorrecte.
nasch

35
Dire qu'il s'agit d'une utilisation incorrecte implique qu'il existe une meilleure alternative. Quelle est cette alternative?
Kyle Delaney

41
VikasGupta a tort, je peux affirmer qu'après de nombreuses années de programmation C # et C ++ hautes performances dans des scénarios non théoriques. Il existe en effet des cas fréquents où l'on crée un dictionnaire, stocke des paires clé-valeur uniques, puis itère sur ces valeurs, qui se sont avérées avoir des clés uniques dans la collection. La création de collections supplémentaires est un moyen vraiment inefficace et coûteux d'éviter l'itération du dictionnaire. Veuillez fournir une bonne alternative comme réponse à la question clarifiant votre point de vue, sinon votre commentaire est assez absurde.
sɐunıɔ ןɐ qɐp

Réponses:


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

29
Et si je ne connais pas exactement le type de clé / valeur dans le dictionnaire. L'utilisation var entryest meilleure dans ce cas, et j'ai donc voté cette réponse sur un deuxième regard au lieu de celui-ci.
Ozair Kafray

93
@OzairKafray utilise varlorsque vous ne connaissez pas le type est généralement une mauvaise pratique.
Nate

52
Cette réponse est supérieure car Pablo n'a pas utilisé par défaut l'utilisation du codeur paresseux "var" qui obscurcit le type de retour.
MonkeyWrench

119
@MonkeyWrench: Meh. Visual Studio sait quel est le type; tout ce que vous avez à faire est de survoler la variable pour le découvrir.
Robert Harvey

31
Si je comprends bien, varne fonctionne que si le type est connu au moment de la compilation. Si Visual Studio connaît le type, vous pouvez également le découvrir.
Kyle Delaney

866

Si vous essayez d'utiliser un dictionnaire générique en C # comme vous utiliseriez un tableau associatif dans une autre langue:

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

Ou, si vous avez seulement besoin d'itérer sur la collection de clés, utilisez

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

Et enfin, si vous êtes uniquement intéressé par les valeurs:

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

(Notez que le varmot-clé est une fonctionnalité facultative C # 3.0 et supérieure, vous pouvez également utiliser le type exact de vos clés / valeurs ici)


11
la fonction var est la plus requise pour votre premier bloc de code :)
nawfal

27
J'apprécie que cette réponse indique que vous pouvez parcourir les clés ou les valeurs de manière explicite.
Rotsiser Mho

11
Je n'aime pas l'utilisation de var ici. Étant donné qu'il ne s'agit que de sucre syntaxique, pourquoi l'utiliser ici? Lorsque quelqu'un essaie de lire le code, il devra sauter le code pour déterminer le type de myDictionary(à moins que ce soit le nom réel bien sûr). Je pense que l'utilisation de var est bonne lorsque le type est évident, par exemple, var x = "some string"mais quand ce n'est pas immédiatement évident, je pense que c'est un codage paresseux qui blesse le lecteur / réviseur de code
James Wierzba

8
vardevrait être utilisé avec parcimonie, à mon avis. Surtout ici, ce n'est pas constructif: le type KeyValuePairest probablement pertinent pour la question.
Sinjai

3
vara un but unique et je ne crois pas que ce soit du sucre «syntaxique». L'utiliser à bon escient est une approche appropriée.
Joshua K

159

Dans certains cas, vous aurez peut-être besoin d'un compteur fourni par l'implémentation de la boucle for. Pour cela, LINQ fournit ElementAtce qui permet ce qui suit:

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

21
Pour utiliser la méthode '.ElementAt', n'oubliez pas: using System.Linq; Ce n'est pas inclus dans fx. classes de test générées automatiquement.
Tinia

14
C'est la voie à suivre si vous modifiez les valeurs associées aux touches. Sinon, une exception est levée lors de la modification et de l'utilisation de foreach ().
Mike de Klerk

27
N'est-ce pas ElementAtune opération O (n)?
Arturo Torres Sánchez

162
Cette réponse ne mérite absolument pas tant de votes positifs. Un dictionnaire n'a pas d'ordre implicite, donc son utilisation .ElementAtdans ce contexte peut conduire à des bugs subtils. Beaucoup plus sérieux est le point d'Arturo ci-dessus. Vous allez itérer les dictionary.Count + 1temps de dictionnaire menant à la complexité O (n ^ 2) pour une opération qui ne devrait être que O (n). Si vous avez vraiment besoin d'un index (si vous le faites, vous utilisez probablement le mauvais type de collection en premier lieu), vous devez dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})plutôt itérer et ne pas utiliser .ElementAtà l'intérieur de la boucle.
dépenser

5
ElementAt - fonctionnement o (n)! Sérieusement? Ceci est l'exemple de la façon dont vous ne devriez pas le faire. Ces nombreux votes positifs?
Mukesh Adhvaryu

96

Selon que vous recherchez les clés ou les valeurs ...

De la Dictionary(TKey, TValue)description de la classe 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

Généralement, demander «la meilleure façon» sans contexte spécifique revient à demander quelle est la meilleure couleur ?

D'une part, il y a beaucoup de couleurs et il n'y a pas de meilleure couleur. Cela dépend du besoin et souvent du goût aussi.

D'un autre côté, il existe de nombreuses façons d'itérer un dictionnaire en C # et il n'y a pas de meilleur moyen. Cela dépend du besoin et souvent du goût aussi.

Manière la plus simple

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

Si vous n'avez besoin que de la valeur (permet de l'appeler item, plus lisible que kvp.Value).

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

Si vous avez besoin d'un ordre de tri spécifique

Généralement, les débutants sont surpris de l'ordre d'énumération d'un dictionnaire.

LINQ fournit une syntaxe concise qui permet de spécifier l'ordre (et bien d'autres choses), par exemple:

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

Encore une fois, vous n'aurez peut-être besoin que de la valeur. LINQ fournit également une solution concise pour:

  • itérer directement sur la valeur (permet de l'appeler item, plus lisible quekvp.Value )
  • mais trié par les clés

C'est ici:

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

Il existe de nombreux autres cas d'utilisation réels que vous pouvez faire à partir de ces exemples. Si vous n'avez pas besoin d'une commande spécifique, respectez simplement la "voie la plus simple" (voir ci-dessus)!


La dernière devrait être .Valueset non une clause de sélection.
Mafii

@Mafii Êtes-vous sûr? Les valeurs renvoyées par OrderBy ne sont pas de type KeyValuePair, elles n'ont pas de Valuechamp. Le type exact que je vois ici est IOrderedEnumerable<KeyValuePair<TKey, TValue>>. Peut-être que vous vouliez dire autre chose? Pouvez-vous écrire une ligne complète montrant ce que vous voulez dire (et la tester)?
Stéphane Gourichon

Je pense que cette réponse contient ce que je veux dire: stackoverflow.com/a/141105/5962841 mais corrigez-moi si j'ai confondu quelque chose
Mafii

1
@Mafii Relisez toute ma réponse, les explications entre les sections de code indiquent le contexte. La réponse que vous mentionnez est comme la deuxième section de code dans ma réponse (aucune commande requise). Là, je viens d'écrire items.Valuecomme vous l'avez suggéré. Dans le cas de la quatrième section que vous avez commentée, le Select()est un moyen de faire foreachénumérer directement les valeurs dans le dictionnaire au lieu des paires clé-valeur. Si vous n'aimez pas le Select()dans ce cas, vous préférerez peut-être la troisième section de code. Le but de la quatrième section est de montrer que l'on peut prétraiter la collecte avec LINQ.
Stéphane Gourichon

1
Si vous le faites, .Keys.Orderby()vous parcourrez une liste de clés. Si c'est tout ce dont vous avez besoin, très bien. Si vous avez besoin de valeurs, alors dans la boucle, vous devrez interroger le dictionnaire sur chaque clé pour obtenir la valeur. Dans de nombreux scénarios, cela ne fera aucune différence pratique. Dans un scénario haute performance, il le fera. Comme je l'ai écrit au début de la réponse: "il existe de nombreuses façons (...) et il n'y a pas de meilleure façon. Cela dépend du besoin et souvent du goût aussi."
Stéphane Gourichon

53

Je dirais que foreach est le moyen standard, bien que cela dépende évidemment de ce que vous recherchez

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

C'est ça que vous cherchez?


42
Um, est-ce que nommer l'élément "valeur" n'est pas assez déroutant? Vous utiliserez généralement une syntaxe comme "value.Key" et "value.Value", ce qui n'est pas très intuitif pour quiconque lira le code, surtout s'ils ne connaissent pas la façon dont le dictionnaire .Net est implémenté .
RenniePet

3
@RenniePet kvpest couramment utilisé pour désigner les instances KeyValuePair lorsque vous parcourez des dictionnaires et des structures de données connexes: foreach(var kvp in myDictionary){....
mbx

37

Vous pouvez également l'essayer sur de gros dictionnaires pour le traitement multithread.

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

8
@WiiMaxx et plus important si ces éléments ne dépendent PAS les uns des autres
Mafii

37

C # 7.0 a introduit les déconstructeurs et si vous utilisez l'application .NET Core 2.0+ , la structureKeyValuePair<>inclut déjà unDeconstruct()pour vous. Vous pouvez donc faire:

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

entrez la description de l'image ici


5
Si vous utilisez .NET Framework, qui au moins jusqu'à 4.7.2 n'a pas le Deconstruct sur KeyValuePair, essayez ceci:foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
nwsmith

29

J'apprécie que cette question a déjà eu beaucoup de réponses mais je voulais apporter un peu de recherche.

L'itération sur un dictionnaire peut être assez lente par rapport à l'itération sur quelque chose comme un tableau. Dans mes tests, une itération sur un tableau a pris 0,015003 secondes tandis qu'une itération sur un dictionnaire (avec le même nombre d'éléments) a pris 0,0365073 secondes, soit 2,4 fois plus longtemps! Bien que j'aie vu des différences beaucoup plus importantes. A titre de comparaison, une liste se situait quelque part entre 0,00215043 secondes.

Cependant, c'est comme comparer des pommes et des oranges. Mon point est que l'itération sur les dictionnaires est lente.

Les dictionnaires sont optimisés pour les recherches, donc dans cet esprit, j'ai créé deux méthodes. L'un fait simplement un foreach, l'autre itère les touches puis lève les yeux.

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

    return "Normal";
}

Celui-ci charge les clés et les parcourt à la place (j'ai également essayé de tirer les clés dans une chaîne [] mais la différence était négligeable.

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

Avec cet exemple, le test normal de foreach a pris 0,0310062 et la version des clés a pris 0,2205441. Charger toutes les clés et itérer sur toutes les recherches est clairement beaucoup plus lent!

Pour un test final, j'ai effectué mon itération dix fois pour voir s'il y a des avantages à utiliser les clés ici (à ce stade, j'étais juste curieux):

Voici la méthode RunTest si cela vous aide à visualiser ce qui se passe.

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

Ici, l'exécution normale de chaque séquence a pris 0,2820564 secondes (environ dix fois plus longtemps qu'une seule itération - comme vous vous en doutez). L'itération sur les clés a pris 2,2249449 secondes.

Modifié pour ajouter: La lecture de certaines des autres réponses m'a fait me demander ce qui se passerait si j'utilisais Dictionnaire au lieu de Dictionnaire. Dans cet exemple, le tableau a pris 0,0120024 secondes, la liste 0,0185037 secondes et le dictionnaire 0,0465093 secondes. Il est raisonnable de s'attendre à ce que le type de données fasse une différence sur la lenteur du dictionnaire.

Quelles sont mes conclusions ?

  • Évitez d'itérer sur un dictionnaire si vous le pouvez, ils sont beaucoup plus lents que d'itérer sur un tableau contenant les mêmes données.
  • Si vous choisissez d'itérer sur un dictionnaire, n'essayez pas d'être trop intelligent, bien que plus lent, vous pourriez faire bien pire que d'utiliser la méthode foreach standard.

11
Vous devriez mesurer avec quelque chose comme StopWatch au lieu de DateTime: hanselman.com/blog/…
Even Mien

2
pourriez-vous s'il vous plaît décrire votre scénario de test, combien d'éléments où dans votre dictionnaire, à quelle fréquence avez-vous exécuté votre scénario pour calculer le temps moyen, ...
WiiMaxx

3
Fait intéressant, vous obtiendrez des résultats différents en fonction des données que vous avez dans le dictionnaire. Lors de l'itération sur le dictionnaire, la fonction énumérateur doit ignorer de nombreux emplacements vides dans le dictionnaire, ce qui le rend plus lent que l'itération sur un tableau. Si le dictionnaire est plein, il y aura moins d'emplacements vides à ignorer que s'il est à moitié vide.
Martin Brown

26

Il y a plein d'options. Mon préféré est par KeyValuePair

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

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

Vous pouvez également utiliser les collections de clés et de valeurs


14

Avec .NET Framework 4.7on peut utiliser la décomposition

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

Pour que ce code fonctionne sur les versions C # inférieures, ajoutez System.ValueTuple NuGet packageet écrivez quelque part

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
Ceci est une erreur. .NET 4.7 est simplement ValueTupleintégré. Il est disponible sous forme de package de nuget pour les versions antérieures. Plus important encore, C # 7.0+ est nécessaire pour que la Deconstructméthode fonctionne comme déconstructeur var (fruit, number) in fruits.
David Arno

13

Depuis C # 7, vous pouvez déconstruire des objets en variables. Je pense que c'est la meilleure façon de parcourir un dictionnaire.

Exemple:

Créez une méthode d'extension KeyValuePair<TKey, TVal>qui la déconstruit:

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

Itérer sur tout Dictionary<TKey, TVal>de la manière suivante

// 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

Vous avez suggéré ci-dessous d'itérer

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

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

Pour info, foreachne fonctionne pas si la valeur est de type objet.


4
Veuillez préciser: foreachne fonctionnera pas si quelle valeur est de type object? Sinon, cela n'a pas beaucoup de sens.
Marc L.

9

Forme la plus simple pour itérer un dictionnaire:

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

9

À l'aide de C # 7 , ajoutez cette méthode d'extension à n'importe quel projet de votre solution:

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


Et utilisez cette syntaxe simple

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


Ou celui-ci, si vous préférez

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


Au lieu du traditionnel

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

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


La méthode d'extension transforme le KeyValuePairde votre IDictionary<TKey, TValue>en un texte fortement typétuple , vous permettant d'utiliser cette nouvelle syntaxe confortable.

Il convertit -juste- les entrées de dictionnaire requises en tuples, donc il ne convertit PAS le dictionnaire entier entuples , donc il n'y a aucun problème de performance lié à cela.

Il n'y a qu'un coût mineur appelant la méthode d'extension pour créer un tupleen comparaison avec l'utilisation KeyValuePairdirecte de, ce qui ne devrait PAS être un problème si vous affectez les KeyValuePairpropriétés de Keyet Valueà de nouvelles variables de boucle de toute façon.

Dans la pratique, cette nouvelle syntaxe convient très bien à la plupart des cas, sauf pour les scénarios de très haut niveau à très hautes performances, où vous avez toujours la possibilité de simplement ne pas l'utiliser à cet endroit spécifique.

Découvrez ceci: Blog MSDN - Nouvelles fonctionnalités dans C # 7


1
Quelle serait la raison de préférer les tuples «confortables» aux paires clé-valeur? Je ne vois pas de gain ici. Votre tuple contient une clé et une valeur, tout comme la paire clé-valeur.
Maarten

4
Salut Maarten, merci pour ta question. Le principal avantage est la lisibilité du code sans effort de programmation supplémentaire. Avec KeyValuePair, il faut toujours utiliser le formulaire kvp.Keyet kvp.Valuepour utiliser respectivement la clé et la valeur. Avec les tuples, vous avez la possibilité de nommer la clé et la valeur comme vous le souhaitez, sans utiliser d'autres déclarations de variables à l'intérieur du bloc foreach. Par exemple, vous pouvez nommer votre clé as factoryNameet la valeur as models, ce qui est particulièrement utile lorsque vous obtenez des boucles imbriquées (dictionnaires de dictionnaires): la maintenance du code devient beaucoup plus facile. Essayez-le! ;-)
sɐunıɔ ןɐ qɐp

7

Je sais que c'est une très vieille question, mais j'ai créé quelques méthodes d'extension qui pourraient être utiles:

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

De cette façon, je peux écrire du code comme ceci:

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

Parfois, si vous avez seulement besoin d'énumérer les valeurs, utilisez la collection de valeurs du dictionnaire:

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

Rapporté par ce post qui déclare que c'est la méthode la plus rapide: http://alexpinsker.blogspot.hk/2010/02/c-fastest-way-to-iterate-over.html


+1 pour apporter des performances. En effet, itérer sur le dictionnaire lui-même inclut des frais généraux si vous n'avez besoin que de valeurs. Cela est principalement dû à la copie de valeurs ou de références dans des structures KeyValuePair.
Jaanus Varus

1
Pour info, le dernier test de ce lien est le test de la mauvaise chose: il mesure l'itération sur les clés, sans tenir compte des valeurs, tandis que les deux tests précédents utilisent la valeur.
Crescent Fresh

5

J'ai trouvé cette méthode dans la documentation de la classe DictionaryBase sur MSDN:

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

Ce fut le seul que j'ai réussi à faire fonctionner correctement dans une classe héritée de DictionaryBase.


3
Cela ressemble à l'utilisation de la version non générique de Dictionary ... c'est-à-dire avant .NET Framework 2.0.
joedotnot

@ joed0tnot: c'est la version non générique utilisée pour les Hashtableobjets
sɐunıɔ ןɐ qɐp

5

foreachest plus rapide et si vous ne faites que répéter ___.Values, c'est aussi plus rapide

entrez la description de l'image ici


1
Pourquoi appelez-vous ContainsKey()la forversion? Cela ajoute une surcharge supplémentaire qui n'est pas présente dans le code que vous comparez. TryGetValue()existe pour remplacer ce modèle "si la clé existe, obtenir l'élément avec la clé". De plus, si dictcontient une plage contiguë d'entiers de 0à dictCount - 1, vous savez que l'indexeur ne peut pas échouer; sinon, dict.Keysc'est ce que vous devriez répéter. De toute façon, non ContainsKey()/ TryGetValue()nécessaire. Enfin, veuillez ne pas publier de captures d'écran du code.
BACON

3

Je profiterai de .NET 4.0+ et fournirai une réponse mise à jour à celle initialement acceptée:

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

3

La manière standard d'itérer sur un dictionnaire, selon la documentation officielle sur MSDN, est:

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

2

J'ai écrit une extension pour faire une boucle sur un dictionnaire.

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

Ensuite, vous pouvez appeler

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

Vous avez défini une ForEachméthode dans laquelle vous avez foreach (...) { }... Semble inutile.
Maarten

1

Si, par exemple, vous souhaitez parcourir la collection de valeurs par défaut, je pense que vous pouvez implémenter IEnumerable <>, où T est le type de l'objet valeurs dans le dictionnaire, et "ceci" est un dictionnaire.

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

1

Comme déjà indiqué dans cette réponse , KeyValuePair<TKey, TValue>implémente une Deconstructméthode commençant par .NET Core 2.0, .NET Standard 2.1 et .NET Framework 5.0 (préversion).

Avec cela, il est possible de parcourir un dictionnaire de manière KeyValuePairagnostique:

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

// ...

foreach (var (key, value) in dictionary)
{
    // ...
}

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

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

2
Il doit y avoir plus de justification / description dans cette réponse. Qu'est-ce que cela AggregateObjectajoute KeyValuePair? Où est «l'itération», comme demandé dans la question?
Marc L.

Sélectionnez itère sur le dictionnaire et nous permet de travailler sur chaque objet. Ce n'est pas aussi général que foreach, mais je l'ai beaucoup utilisé. Ma réponse méritait-elle vraiment un vote négatif?
Pixar

Non, Select utilise l' itération pour effectuer le résultat, mais n'est pas un itérateur lui-même. Les types de choses pour lesquelles iteration ( foreach) est utilisé - en particulier les opérations avec effets secondaires - sont hors de portée de Linq, y compris Select. Le lambda ne fonctionnera pas tant qu'il n'aura pas aggregateObjectCollectionété énuméré. Si cette réponse est considérée comme un "premier chemin" (c'est-à-dire utilisé avant une ligne droite foreach), elle encourage les mauvaises pratiques. Dans certains cas, il peut y avoir des opérations Linq qui sont utiles avant d' itérer un dictionnaire, mais cela ne répond pas à la question posée.
Marc L.

0

Je voulais juste ajouter mes 2 cents, car la plupart des réponses se rapportent à foreach-loop. Veuillez jeter un œil au code suivant:

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

Même si cela ajoute un appel supplémentaire de '.ToList ()', il pourrait y avoir une légère amélioration des performances (comme indiqué ici foreach vs someList.Foreach () {} ), surtout lorsque vous travaillez avec de gros dictionnaires et que l'exécution en parallèle n'est pas option / n'aura aucun effet.

Notez également que vous ne pourrez pas attribuer de valeurs à la propriété 'Value' dans une boucle foreach. D'un autre côté, vous pourrez également manipuler la «clé», ce qui pourrait vous causer des problèmes lors de l'exécution.

Lorsque vous souhaitez simplement "lire" les clés et les valeurs, vous pouvez également utiliser IEnumerable.Select ().

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

5
La copie de la collection entière sans aucune raison n'améliorera pas les performances. Cela ralentira considérablement le code et doublera l'empreinte mémoire du code qui ne devrait pratiquement consommer aucune mémoire supplémentaire.
Servy

J'évite la méthode des effets secondaires «List.ForEach»: foreachforce la visibilité des effets secondaires vers le haut, là où elle appartient.
user2864740

0

Dictionary <TKey, TValue> C'est une classe de collection générique en c # et elle stocke les données dans le format de valeur de clé. La clé doit être unique et elle ne peut pas être nulle tandis que la valeur peut être dupliquée et nulle. Comme chaque élément du dictionnaire est traitée comme une structure KeyValuePair <TKey, TValue> représentant une clé et sa valeur. et donc nous devons prendre le type d'élément KeyValuePair <TKey, TValue> pendant l'itération de l'élément. Voici l'exemple.

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

Si vous souhaitez utiliser for loop, vous pouvez le faire:

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

Quel est l'avantage de cela, cependant? C'est du code plus long qu'une foreachboucle et de moins bonnes performances, car il new List<string>(dictionary.Keys)faudra répéter les dictionary.Counttemps avant même que vous ayez la possibilité de le répéter vous-même. Mettant de côté que demander "la meilleure façon" est subjectif, je ne vois pas comment cela pourrait être qualifié de "meilleure façon" ou de "voie standard" que la question cherche. Pour "Si vous voulez utiliser pour la boucle ...", je répondrais par " Ne pas utiliser de forboucle."
BACON

Si vous avez une grande collection et que les opérations sont lentes dans foreach et si votre collection peut changer lorsque vous itérez, cela vous protège contre l'erreur "collection a changé" et cette solution a de meilleures performances que l'utilisation d'ElementAt.
Seçkin Durgay

Je conviens qu'éviter les exceptions «Collection a été modifiée» est une raison de le faire, bien que ce cas spécial n'ait pas été mentionné dans la question et que l'on puisse toujours le faire foreach (var pair in dictionary.ToArray()) { }. Pourtant, je pense qu'il serait bon de préciser dans la réponse le ou les scénarios spécifiques dans lesquels on voudrait utiliser ce code et les implications de le faire.
BACON

oui vous avez raison mais il y a une réponse ici avec ElementAt et il a une très bonne réputation et j'ai entré cette réponse :)
Seçkin Durgay

-2

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

Je vois que vous appelez ToList()car ForEach()n'est défini que sur la List<>classe, mais pourquoi faire tout cela au lieu de juste foreach (var pair in dict) { }? Je dirais que c'est encore plus simple et n'a pas les mêmes implications mémoire / performances. Cette solution exacte a déjà été proposée dans cette réponse il y a 3,5 ans, de toute façon.
BACON

Juste pour la visualisation du code, cela pourrait vraiment être fait comme vous l'avez indiqué, je voulais juste le présenter d'une manière différente et dans ma vue maigre, je m'excuse de ne pas voir la réponse indiquée, un câlin
Luiz Fernando Corrêa Leite

-4

en plus des postes les plus élevés où il y a une discussion entre l'utilisation

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

ou

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

le plus complet est le suivant car vous pouvez voir le type de dictionnaire depuis l'initialisation, kvp est 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
La création et la copie d'un deuxième dictionnaire n'est pas une solution valable pour la lisibilité du code. En fait, je dirais que cela rendrait le code plus difficile à comprendre parce que maintenant vous devez vous demander: "Pourquoi le dernier gars a-t-il créé un deuxième dictionnaire?" Si vous voulez être plus verbeux, utilisez simplement la première option.
Matthew Goulart

Je voulais juste vous montrer que lorsque déclic avant pour chacun, l'utilisation dans le foreach est claire de la déclaration
BigChief
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.