web-dev-qa-db-fra.com

Quel est le meilleur moyen de parcourir un dictionnaire?

J'ai vu différentes façons de parcourir un dictionnaire en C #. Y a-t-il un moyen standard?

2346
Jake Stewart
foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}
3370
Pablo Fernandez

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

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

Ou, si vous devez seulement parcourir la collection de clés, utilisez

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

Et enfin, si vous ne vous intéressez qu'aux valeurs:

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

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

783
Jacob

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

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

Cela dépend si vous êtes après les clés ou les valeurs ...

Depuis le MSDN Dictionary(TKey, TValue) Description de la classe:

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

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.

D'autre part, il existe de nombreuses façons de parcourir un dictionnaire en C # et il n'y a pas de meilleure façon. Cela dépend du besoin et souvent du goût.

Manière la plus simple

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

Si vous avez seulement besoin 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

En règle générale, les débutants sont surpris par 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 que kvp.Value)
  • mais triés 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 utiliser à partir de ces exemples. Si vous n'avez pas besoin d'une commande spécifique, tenez-vous-en au "moyen le plus direct" (voir ci-dessus)!

76
Stéphane Gourichon

Je dirais que foreach est la méthode standard, bien que cela dépende évidemment de ce que vous recherchez.

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

Est-ce ce que vous cherchez?

47
George Mauer

Vous pouvez également essayer ceci sur de gros dictionnaires pour un traitement multithread.

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

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

25
theo

J'apprécie que cette question ait déjà suscité beaucoup de réponses, mais je voulais lancer un peu de recherche.

Itérer sur un dictionnaire peut être assez lent par rapport à itérer sur quelque chose comme un tableau. Dans mes tests, une itération sur un tableau prenait 0,015003 secondes alors qu'une itération sur un dictionnaire (avec le même nombre d'éléments) prenait 0,0365073 secondes, soit 2,4 fois plus longtemps! Bien que j'ai vu des différences beaucoup plus grandes. À titre de comparaison, une liste se situait quelque part entre 0,00215043 seconde.

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

Les dictionnaires sont optimisés pour les recherches, c'est pourquoi j'ai créé deux méthodes. L'un fait simplement un foreach, l'autre itère les clés 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 itère à 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";
}

Dans cet exemple, le test foreach normal prenait 0.0310062 et la version de la clé prenait 0.2205441. Charger toutes les clés et parcourir toutes les recherches est clairement beaucoup plus lent!

Pour un dernier test, j'ai effectué mon itération dix fois pour voir s'il y avait un quelconque avantage à utiliser les clés ici (à ce stade, j'étais simplement 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, le cycle normal de chaque opération a pris 0,2820564 secondes (environ dix fois plus longtemps qu’une seule itération - comme on pouvait s’y attendre). L'itération sur les clés a pris 2,2249449 secondes.

Edited To Add: La lecture de certaines des autres réponses m'a amené à me demander ce qui se passerait si j'utilisais Dictionary au lieu de Dictionary. Dans cet exemple, le tableau prenait 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 influe sur la lenteur du dictionnaire.

Quelles sont mes conclusions?

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

C # 7. introduit Déconstructeurs et si vous utilisez . NET Core 2.0+ Application, la structure KeyValuePair<> inclut déjà une Deconstruct() pour vous. Alors tu peux 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}");
}

enter image description here

19
Jaider

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

FYI, foreach ne fonctionne pas si la valeur est de type objet.

11
Khushi

Avec .NET Framework 4.7 on peut utiliser 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 package et é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;
    }
}
11
Pavel

En utilisant 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 KeyValuePair de votre IDictionary<TKey, TValue> en un _Tuple fortement typé, vous permettant d'utiliser cette nouvelle syntaxe confortable.

Il convertit -just- les entrées de dictionnaire requises en tuples. Il ne convertit donc PAS le dictionnaire entier en tuples. Il n'y a donc pas de problème de performances lié à cela.

L'appel de la méthode d'extension pour créer un Tuple par rapport à l'utilisation directe du KeyValuePair n'implique, de manière coûteuse, qu'un problème si vous affectez de toute façon les propriétés KeyValuePair à Key et Value à de nouvelles variables de boucle.

En pratique, cette nouvelle syntaxe convient très bien dans la plupart des cas, à l'exception des scénarios de performances ultra-hautes performances de bas niveau, où vous avez toujours la possibilité de simplement ne pas l'utiliser à cet endroit spécifique.

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

8

La forme la plus simple pour itérer un dictionnaire:

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

Parfois, si vous n’avez besoin que d’énumérer les valeurs, utilisez la collection de valeurs du dictionnaire:

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

Signalé par cet article 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

6
ender

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
}

C'était le seul que je pouvais obtenir de fonctionner correctement dans une classe héritée de DictionaryBase.

5
Zannjaminderson

À partir de C # 7, vous pouvez décomposer des objets en variables. Je crois que c'est le meilleur moyen de parcourir un dictionnaire.

Exemple:

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

public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey, out TVal val)
{
   key = pair.Key;
   val = 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}");
}
3
Domn Werner

La méthode standard pour parcourir un dictionnaire, selon la documentation officielle sur MSDN, est la suivante:

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

Je vais tirer parti de .NET 4.0+ et fournir une réponse mise à jour à celle initialement acceptée:

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

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

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

Je sais que la question est très ancienne, mais j’ai créé des 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););
2
boca

J'ai écrit une extension à boucler 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));
1
Steven Delrue
var dictionary = new Dictionary<string, int>
{
    { "Key", 12 }
};

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

Je voulais juste ajouter mon 2 cent, car la plupart des réponses concernent foreach-loop. S'il vous plaît, jetez un oeil sur le 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));
});

Bien que 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 Les dictionnaires et l'utilisation en parallèle ne sont pas une option/n'auront aucun effet.

Veuillez également noter que vous ne pourrez pas affecter de valeurs à la propriété 'Value' dans une boucle foreach. D'autre part, vous pourrez également manipuler la "clé", ce qui peut éventuellement vous causer des problèmes lors de l'exécution.

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

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

Dictionary <TKey, TValue> C'est une classe de collection générique en c # et stocke les données dans le format de valeur de clé.Key doit être unique et ne peut pas être null alors que la valeur peut être dupliquée et nulle. Chaque élément du dictionnaire étant traité comme une structure KeyValuePair <TKey, TValue> représentant une clé et sa valeur. et par conséquent nous devrions prendre le type d'élément KeyValuePair <TKey, TValue> lors de l'itération de l'élément .Ci-dessous, 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
Sheo Dayal Singh

Les trois méthodes différentes d'itération sur le dictionnaire.

Méthode 1: utilisation de la boucle for

for (int i = 0; i < DemotestDictionary.Count; i++ )
 {
 int key = i;
 string value = DemotestDictionary[i];
 // Do something here
 }

Méthode 2: utiliser chaque itération

foreach (var entry in DemotestDictionary)
 {
 int key = entry.Key;
 string value = entry.Value;
 // Do something here
 } 

Méthode 3-pour chaque itération à l'aide de KeyValuePair

foreach (KeyValuePair<int, string> entry in DemotestDictionary)
 {
 int key = entry.Key;
 string value = entry.Value;
// Do something here
 }
0
Sunil Dhappadhule