J'ai vu plusieurs façons différentes d'itérer sur un dictionnaire en C #. Existe-t-il une méthode standard?
J'ai vu plusieurs façons différentes d'itérer sur un dictionnaire en C #. Existe-t-il une méthode standard?
Réponses:
foreach(KeyValuePair<string, string> entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
var entry
est meilleure dans ce cas, et j'ai donc voté cette réponse sur un deuxième regard au lieu de celui-ci.
var
lorsque vous ne connaissez pas le type est généralement une mauvaise pratique.
var
ne 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.
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 var
mot-clé est une fonctionnalité facultative C # 3.0 et supérieure, vous pouvez également utiliser le type exact de vos clés / valeurs ici)
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
var
devrait être utilisé avec parcimonie, à mon avis. Surtout ici, ce n'est pas constructif: le type KeyValuePair
est probablement pertinent pour la question.
var
a un but unique et je ne crois pas que ce soit du sucre «syntaxique». L'utiliser à bon escient est une approche appropriée.
Dans certains cas, vous aurez peut-être besoin d'un compteur fourni par l'implémentation de la boucle for. Pour cela, LINQ fournit ElementAt
ce 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;
}
ElementAt
une opération O (n)?
.ElementAt
dans 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 + 1
temps 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.
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);
}
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.
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)
}
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:
item
, plus lisible quekvp.Value
)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)!
.Values
et non une clause de sélection.
Value
champ. 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)?
items.Value
comme 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.
.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."
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?
kvp
est 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){...
.
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}");
}
foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
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 ?
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
Avec .NET Framework 4.7
on 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 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;
}
}
ValueTuple
inté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 Deconstruct
méthode fonctionne comme déconstructeur var (fruit, number) in fruits
.
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}");
}
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, foreach
ne fonctionne pas si la valeur est de type objet.
foreach
ne fonctionnera pas si quelle valeur est de type object
? Sinon, cela n'a pas beaucoup de sens.
À 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 KeyValuePair
de 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 tuple
en comparaison avec l'utilisation KeyValuePair
directe de, ce qui ne devrait PAS être un problème si vous affectez les KeyValuePair
propriétés de Key
et 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
kvp.Key
et kvp.Value
pour 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 factoryName
et 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! ;-)
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););
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
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.
Hashtable
objets
ContainsKey()
la for
version? 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 dict
contient une plage contiguë d'entiers de 0
à dictCount - 1
, vous savez que l'indexeur ne peut pas échouer; sinon, dict.Keys
c'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.
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));
ForEach
méthode dans laquelle vous avez foreach (...) { }
... Semble inutile.
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();
}
Comme déjà indiqué dans cette réponse , KeyValuePair<TKey, TValue>
implémente une Deconstruct
mé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 KeyValuePair
agnostique:
var dictionary = new Dictionary<int, string>();
// ...
foreach (var (key, value) in dictionary)
{
// ...
}
var dictionary = new Dictionary<string, int>
{
{ "Key", 12 }
};
var aggregateObjectCollection = dictionary.Select(
entry => new AggregateObject(entry.Key, entry.Value));
AggregateObject
ajoute KeyValuePair
? Où est «l'itération», comme demandé dans la question?
foreach
, mais je l'ai beaucoup utilisé. Ma réponse méritait-elle vraiment un vote négatif?
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.
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 } );
foreach
force la visibilité des effets secondaires vers le haut, là où elle appartient.
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);
}
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];
}
foreach
boucle et de moins bonnes performances, car il new List<string>(dictionary.Keys)
faudra répéter les dictionary.Count
temps 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 for
boucle."
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.
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}") );
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.
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
}