He visto algunas formas diferentes de iterar sobre un diccionario en C #. ¿Hay una forma estándar?
c#
dictionary
loops
Jake Stewart
fuente
fuente
Respuestas:
fuente
var entry
es mejor en ese caso, y así voté esta respuesta en una segunda mirada en lugar de la anterior.var
cuando no sabes el tipo es generalmente una mala práctica.var
solo funciona si el tipo se conoce en tiempo de compilación. Si Visual Studio conoce el tipo, también está disponible para que lo descubras.Si está intentando usar un Diccionario genérico en C # como usaría una matriz asociativa en otro idioma:
O, si solo necesita iterar sobre la colección de claves, use
Y, por último, si solo le interesan los valores:
(Tenga en cuenta que la
var
palabra clave es una característica opcional C # 3.0 y superior, también puede usar el tipo exacto de sus claves / valores aquí)fuente
myDictionary
(a menos que ese sea el nombre real, por supuesto). Creo que usando var es buena cuando el tipo es evidente, por ejemplovar x = "some string"
, pero cuando no es inmediatamente obvio creo que es perezosa de codificación que perjudica el lector de código / revisorvar
debería usarse con moderación, en mi opinión. Particularmente aquí, no es constructivo: el tipoKeyValuePair
es probablemente relevante para la pregunta.var
tiene un propósito único y no creo que sea azúcar 'sintáctico'. Usarlo a propósito es un enfoque apropiado.En algunos casos, puede necesitar un contador que puede proporcionar la implementación for-loop. Para eso, LINQ proporciona lo
ElementAt
que permite lo siguiente:fuente
ElementAt
una operación O (n)?.ElementAt
en este contexto puede generar errores sutiles. Mucho más serio es el punto anterior de Arturo. Estará iterando losdictionary.Count + 1
tiempos del diccionario que conducen a la complejidad O (n ^ 2) para una operación que solo debería ser O (n). Si realmente necesita un índice (si lo necesita, probablemente esté usando el tipo de colección incorrecto en primer lugar), debe iterar en sudictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})
lugar y no usarlo.ElementAt
dentro del bucle.Depende de si buscas las claves o los valores ...
De la
Dictionary(TKey, TValue)
descripción de la clase MSDN :fuente
En general, preguntar por "la mejor manera" sin un contexto específico es como preguntar cuál es el mejor color .
Por un lado, hay muchos colores y no hay mejor color. Depende de la necesidad y, a menudo, también del gusto.
Por otro lado, hay muchas formas de iterar sobre un Diccionario en C # y no hay la mejor manera. Depende de la necesidad y, a menudo, también del gusto.
La forma más directa
Si solo necesita el valor (permite llamarlo
item
, más legible quekvp.Value
).Si necesita un orden de clasificación específico
En general, los principiantes se sorprenden del orden de enumeración de un diccionario.
LINQ proporciona una sintaxis concisa que permite especificar el orden (y muchas otras cosas), por ejemplo:
De nuevo, es posible que solo necesite el valor. LINQ también proporciona una solución concisa para:
item
, más legible quekvp.Value
)Aquí está:
Hay muchos más casos de uso del mundo real que puede hacer con estos ejemplos. Si no necesita un pedido específico, ¡simplemente adhiérase a la "forma más directa" (ver arriba)!
fuente
.Values
y no una cláusula select.Value
campo. El tipo exacto que veo aquí esIOrderedEnumerable<KeyValuePair<TKey, TValue>>
. ¿Quizás quisiste decir algo más? ¿Puedes escribir una línea completa que muestre lo que quieres decir (y probarlo)?items.Value
como sugeriste. En el caso de la cuarta sección que comentó, estaSelect()
es una formaforeach
de enumerar directamente los valores en el diccionario en lugar de los pares clave-valor. Si de alguna manera no le gustaSelect()
en este caso, puede preferir la tercera sección de código. El objetivo de la cuarta sección es mostrar que uno puede preprocesar la colección con LINQ..Keys.Orderby()
, iterará en una lista de claves. Si eso es todo lo que necesitas, está bien. Si necesita valores, entonces en el ciclo tendría que consultar el diccionario en cada clave para obtener el valor. En muchos escenarios no hará una diferencia práctica. En el escenario de alto rendimiento, lo hará. Como escribí al comienzo de la respuesta: "hay muchas formas (...) y no hay una mejor manera. Depende de la necesidad y, a menudo, también del gusto".Diría que foreach es la forma estándar, aunque obviamente depende de lo que estés buscando
¿Es eso lo que estás buscando?
fuente
kvp
se utiliza comúnmente para nombrar casos KeyValuePair cuando iterar sobre los diccionarios y estructuras de datos relacionados:foreach(var kvp in myDictionary){...
.También puede probar esto en grandes diccionarios para el procesamiento multiproceso.
fuente
C # 7.0 introdujo Deconstructors y si está utilizando la aplicación .NET Core 2.0+ , la estructura
KeyValuePair<>
ya incluye unaDeconstruct()
para usted. Entonces puedes hacer:fuente
foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
Aprecio que esta pregunta ya haya tenido muchas respuestas, pero quería investigar un poco.
Iterar sobre un diccionario puede ser bastante lento en comparación con iterar sobre algo como una matriz. En mis pruebas, una iteración sobre una matriz tomó 0.015003 segundos, mientras que una iteración sobre un diccionario (con el mismo número de elementos) tomó 0.0365073 segundos, ¡2.4 veces más! Aunque he visto diferencias mucho mayores. A modo de comparación, una lista estaba en algún punto intermedio en 0.00215043 segundos.
Sin embargo, eso es como comparar manzanas y naranjas. Mi punto es que iterar sobre los diccionarios es lento.
Los diccionarios están optimizados para las búsquedas, así que con eso en mente, he creado dos métodos. Uno simplemente hace un foreach, el otro itera las teclas y luego mira hacia arriba.
Éste carga las claves y las repite en su lugar (también intenté tirar de las claves en una cadena [] pero la diferencia fue insignificante.
Con este ejemplo, la prueba foreach normal tomó 0.0310062 y la versión de claves tomó 0.2205441. ¡Cargar todas las claves e iterar sobre todas las búsquedas es claramente MUCHO más lento!
Para una prueba final, realicé mi iteración diez veces para ver si hay algún beneficio al usar las teclas aquí (en este punto tenía curiosidad):
Aquí está el método RunTest si eso te ayuda a visualizar lo que está sucediendo.
Aquí la ejecución foreach normal tardó 0.2820564 segundos (aproximadamente diez veces más de lo que tomó una sola iteración, como era de esperar). La iteración sobre las teclas tomó 2.2249449 segundos.
Editado para agregar: Leer algunas de las otras respuestas me hizo preguntarme qué pasaría si usara Dictionary en lugar de Dictionary. En este ejemplo, la matriz tardó 0.0120024 segundos, la lista 0.0185037 segundos y el diccionario 0.0465093 segundos. Es razonable esperar que el tipo de datos haga una diferencia en cuanto más lento es el diccionario.
¿Cuáles son mis conclusiones ?
fuente
Hay muchas opciones Mi favorito personal es KeyValuePair
También puede usar las Colecciones de claves y valores
fuente
Con
.NET Framework 4.7
uno se puede usar descomposiciónPara que este código funcione en versiones inferiores de C #, agregue
System.ValueTuple NuGet package
y escriba en alguna partefuente
ValueTuple
incorporado. Está disponible como un paquete nuget para versiones anteriores. Más importante aún, se necesita C # 7.0+ para que elDeconstruct
método funcione como deconstructorvar (fruit, number) in fruits
.A partir de C # 7, puede deconstruir objetos en variables. Creo que esta es la mejor manera de iterar sobre un diccionario.
Ejemplo:
Cree un método de extensión
KeyValuePair<TKey, TVal>
que lo deconstruya:Iterar sobre cualquiera
Dictionary<TKey, TVal>
de la siguiente manerafuente
Sugeriste a continuación para iterar
FYI,
foreach
no funciona si los valores son de tipo objeto.fuente
foreach
¿no funcionará si qué valor es de tipoobject
? De lo contrario, esto no tiene mucho sentido.La forma más simple de iterar un diccionario:
fuente
Con C # 7 , agregue este método de extensión a cualquier proyecto de su solución:
Y usa esta sintaxis simple
O este, si lo prefieres
En lugar de lo tradicional
El método de extensión transforma el
KeyValuePair
de suIDictionary<TKey, TValue>
en un tipo fuertetuple
, lo que le permite utilizar esta nueva sintaxis cómoda.Convierte -solo- las entradas de diccionario requeridas
tuples
, por lo que NO convierte todo el diccionario atuples
, por lo que no hay problemas de rendimiento relacionados con eso.Hay un costo menor que llama al método de extensión para crear un
tuple
en comparación con el usoKeyValuePair
directo, lo cual NO debería ser un problema si está asignando lasKeyValuePair
propiedades de 's'Key
yValue
a nuevas variables de bucle de todos modos.En la práctica, esta nueva sintaxis se adapta muy bien para la mayoría de los casos, excepto para escenarios de rendimiento ultra alto de bajo nivel, donde todavía tiene la opción de simplemente no usarlo en ese lugar específico.
Mira esto: Blog de MSDN - Nuevas funciones en C # 7
fuente
kvp.Key
ykvp.Value
para usar respectivamente clave y valor. Con las tuplas obtienes la flexibilidad de nombrar la clave y el valor como desees, sin usar más declaraciones de variables dentro del bloque foreach. Por ejemplo, puede nombrar su clave comofactoryName
, y el valor comomodels
, lo cual es especialmente útil cuando obtiene bucles anidados (diccionarios de diccionarios): el mantenimiento del código se vuelve mucho más fácil. ¡Solo pruébalo! ;-)Sé que esta es una pregunta muy antigua, pero creé algunos métodos de extensión que podrían ser útiles:
De esta manera puedo escribir código como este:
fuente
A veces, si solo necesita enumerar los valores, use la colección de valores del diccionario:
Reportado por esta publicación que dice que es el método más rápido: http://alexpinsker.blogspot.hk/2010/02/c-fastest-way-to-iterate-over.html
fuente
Encontré este método en la documentación de la clase DictionaryBase en MSDN:
Este fue el único que pude hacer funcionar correctamente en una clase que heredó de DictionaryBase.
fuente
Hashtable
foreach
es más rápido y si solo repite___.Values
, también es más rápidofuente
ContainsKey()
en lafor
versión? Eso agrega una sobrecarga adicional que no está presente en el código con el que se compara.TryGetValue()
existe para reemplazar ese patrón exacto "si la clave existe, obtener el elemento con la clave" Además, sidict
contiene un rango contiguo de enteros de0
adictCount - 1
, usted sabe que el indexador no puede fallar; de lo contrario,dict.Keys
es lo que debería estar iterando. De cualquier manera, noContainsKey()
/TryGetValue()
necesario. Por último, no publique capturas de pantalla de código.Aprovecharé .NET 4.0+ y proporcionaré una respuesta actualizada a la aceptada originalmente:
fuente
La forma estándar de iterar sobre un diccionario, de acuerdo con la documentación oficial en MSDN es:
fuente
Escribí una extensión para recorrer un diccionario.
Entonces puedes llamar
fuente
ForEach
método en el que tienesforeach (...) { }
... Parece innecesario.Si dice, desea iterar sobre la colección de valores de forma predeterminada, creo que puede implementar IEnumerable <>, donde T es el tipo de objeto de valores en el diccionario y "this" es un diccionario.
fuente
Como ya se señaló en esta respuesta ,
KeyValuePair<TKey, TValue>
implementa unDeconstruct
método que comienza en .NET Core 2.0, .NET Standard 2.1 y .NET Framework 5.0 (versión preliminar).Con esto, es posible iterar a través de un diccionario de forma
KeyValuePair
agnóstica:fuente
fuente
AggregateObject
agregaKeyValuePair
? ¿Dónde está la "iteración", como se solicita en la pregunta?foreach
, pero lo he usado mucho. ¿Mi respuesta realmente merecía un voto negativo?Select
usa la iteración para efectuar el resultado, pero no es un iterador en sí mismo. Los tipos de cosas para los queforeach
se usa la iteración ( ), especialmente las operaciones con efectos secundarios, están fuera del alcance de Linq, incluidoSelect
. La lambda no se ejecutará hasta queaggregateObjectCollection
se enumere realmente. Si esta respuesta se toma como un "primer camino" (es decir, se usa antes de una rectaforeach
), fomenta las malas prácticas. Situacionalmente, puede haber operaciones de Linq que sean útiles antes de iterar un diccionario, pero eso no responde a la pregunta como se hizo.Solo quería agregar mi 2 centavo, ya que la mayoría de las respuestas se relacionan con foreach-loop. Por favor, eche un vistazo al siguiente código:
Aunque esto agrega una llamada adicional de '.ToList ()', podría haber una ligera mejora en el rendimiento (como se señala aquí foreach vs someList.Foreach () {} ), espacialmente cuando se trabaja con grandes diccionarios y se ejecuta en paralelo no La opción / no tendrá ningún efecto.
Además, tenga en cuenta que no podrá asignar valores a la propiedad 'Valor' dentro de un bucle foreach. Por otro lado, también podrás manipular la 'Clave', posiblemente metiéndote en problemas en el tiempo de ejecución.
Cuando solo quiere "leer" claves y valores, también puede usar IEnumerable.Select ().
fuente
foreach
fuerza la visibilidad de los efectos secundarios hacia arriba, donde pertenece.Dictionary <TKey, TValue> Es una clase de colección genérica en c # y almacena los datos en el formato de valor de clave. La clave debe ser única y no puede ser nula, mientras que el valor puede ser duplicado y nulo. Como cada elemento del diccionario es tratada como KeyValuePair <TKey, TValue> estructura que representa una clave y su valor. y, por lo tanto, deberíamos tomar el tipo de elemento KeyValuePair <TKey, TValue> durante la iteración del elemento. A continuación se muestra el ejemplo.
fuente
Si desea usar for loop, puede hacer esto:
fuente
foreach
bucle y un rendimiento peor porquenew List<string>(dictionary.Keys)
repetirádictionary.Count
veces antes de que incluso tenga la oportunidad de repetirlo usted mismo. Dejando de lado que pedir "la mejor manera" es subjetiva, no veo cómo esto calificaría como la "mejor manera" o la "forma estándar" que busca la pregunta. Para "Si desea usar para el bucle ...", respondería con " No use unfor
bucle".foreach (var pair in dictionary.ToArray()) { }
. Aún así, creo que sería bueno aclarar en la respuesta los escenarios específicos en los que uno desearía usar este código y las implicaciones de hacerlo.simple con linq
fuente
ToList()
porqueForEach()
solo se define en laList<>
clase, pero ¿por qué hacer todo eso en lugar de soloforeach (var pair in dict) { }
? Yo diría que es aún más simple y no tiene las mismas implicaciones de memoria / rendimiento. Esta solución exacta ya fue propuesta en esta respuesta de hace 3.5 años, de todos modos.Además de las publicaciones de más alto rango donde hay una discusión entre el uso
o
el más completo es el siguiente porque puede ver el tipo de diccionario desde la inicialización, kvp es KeyValuePair
fuente