J'ai vu plusieurs façons différentes d'itérer sur un dictionnaire en C #. Existe-t-il une méthode standard?
c#
dictionary
loops
Jake Stewart
la source
la source
Réponses:
la source
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:
Ou, si vous avez seulement besoin d'itérer sur la collection de clés, utilisez
Et enfin, si vous êtes uniquement intéressé par les valeurs:
(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)la source
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 codevar
devrait être utilisé avec parcimonie, à mon avis. Surtout ici, ce n'est pas constructif: le typeKeyValuePair
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:la source
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 lesdictionary.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 devezdictionary.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 :la source
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
Si vous n'avez besoin que de la valeur (permet de l'appeler
item
, plus lisible quekvp.Value
).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:
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:
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 source
.Values
et non une clause de sélection.Value
champ. Le type exact que je vois ici estIOrderedEnumerable<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, leSelect()
est un moyen de faireforeach
énumérer directement les valeurs dans le dictionnaire au lieu des paires clé-valeur. Si vous n'aimez pas leSelect()
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
C'est ça que vous cherchez?
la source
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){...
.Vous pouvez également l'essayer sur de gros dictionnaires pour le traitement multithread.
la source
C # 7.0 a introduit les déconstructeurs et si vous utilisez l'application .NET Core 2.0+ , la structure
KeyValuePair<>
inclut déjà unDeconstruct()
pour vous. Vous pouvez donc faire:la source
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.
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.
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.
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 ?
la source
Il y a plein d'options. Mon préféré est par KeyValuePair
Vous pouvez également utiliser les collections de clés et de valeurs
la source
Avec
.NET Framework 4.7
on peut utiliser la décompositionPour que ce code fonctionne sur les versions C # inférieures, ajoutez
System.ValueTuple NuGet package
et écrivez quelque partla source
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 laDeconstruct
méthode fonctionne comme déconstructeurvar (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:Itérer sur tout
Dictionary<TKey, TVal>
de la manière suivantela source
Vous avez suggéré ci-dessous d'itérer
Pour info,
foreach
ne fonctionne pas si la valeur est de type objet.la source
foreach
ne fonctionnera pas si quelle valeur est de typeobject
? Sinon, cela n'a pas beaucoup de sens.Forme la plus simple pour itérer un dictionnaire:
la source
À l'aide de C # 7 , ajoutez cette méthode d'extension à n'importe quel projet de votre solution:
Et utilisez cette syntaxe simple
Ou celui-ci, si vous préférez
Au lieu du traditionnel
La méthode d'extension transforme le
KeyValuePair
de votreIDictionary<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'utilisationKeyValuePair
directe de, ce qui ne devrait PAS être un problème si vous affectez lesKeyValuePair
propriétés deKey
etValue
à 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
la source
kvp.Key
etkvp.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é asfactoryName
et la valeur asmodels
, 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:
De cette façon, je peux écrire du code comme ceci:
la source
Parfois, si vous avez seulement besoin d'énumérer les valeurs, utilisez la collection de valeurs du dictionnaire:
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
la source
J'ai trouvé cette méthode dans la documentation de la classe DictionaryBase sur MSDN:
Ce fut le seul que j'ai réussi à faire fonctionner correctement dans une classe héritée de DictionaryBase.
la source
Hashtable
objetsforeach
est plus rapide et si vous ne faites que répéter___.Values
, c'est aussi plus rapidela source
ContainsKey()
lafor
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, sidict
contient une plage contiguë d'entiers de0
à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, nonContainsKey()
/TryGetValue()
nécessaire. Enfin, veuillez ne pas publier de captures d'écran du code.Je profiterai de .NET 4.0+ et fournirai une réponse mise à jour à celle initialement acceptée:
la source
La manière standard d'itérer sur un dictionnaire, selon la documentation officielle sur MSDN, est:
la source
J'ai écrit une extension pour faire une boucle sur un dictionnaire.
Ensuite, vous pouvez appeler
la source
ForEach
méthode dans laquelle vous avezforeach (...) { }
... 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.
la source
Comme déjà indiqué dans cette réponse ,
KeyValuePair<TKey, TValue>
implémente uneDeconstruct
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:la source
la source
AggregateObject
ajouteKeyValuePair
? 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 comprisSelect
. Le lambda ne fonctionnera pas tant qu'il n'aura pasaggregateObjectCollection
été énuméré. Si cette réponse est considérée comme un "premier chemin" (c'est-à-dire utilisé avant une ligne droiteforeach
), 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:
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 ().
la source
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.
la source
Si vous souhaitez utiliser for loop, vous pouvez le faire:
la source
foreach
boucle et de moins bonnes performances, car ilnew List<string>(dictionary.Keys)
faudra répéter lesdictionary.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 defor
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
la source
ToList()
carForEach()
n'est défini que sur laList<>
classe, mais pourquoi faire tout cela au lieu de justeforeach (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
ou
le plus complet est le suivant car vous pouvez voir le type de dictionnaire depuis l'initialisation, kvp est KeyValuePair
la source