¿Cuál es la mejor manera de iterar sobre un diccionario?

2628

He visto algunas formas diferentes de iterar sobre un diccionario en C #. ¿Hay una forma estándar?

Jake Stewart
fuente
108
Me sorprende que haya muchas respuestas a esta pregunta, junto con una votada 923 veces ... (usa foreach, por supuesto) ... Yo diría, o al menos agregaría que si tiene que iterar sobre un diccionario, es probable que esté usarlo de manera incorrecta / inapropiada ... Tuve que hacer este comentario, porque he visto que se abusa de los diccionarios de maneras que, en mi humilde opinión, no eran apropiadas ... Sí, puede haber circunstancias raras cuando itera el diccionario, en lugar de buscar, que es lo que está diseñado para ... Por favor, tenga esto en cuenta, antes de preguntarse cómo iterar sobre un diccionario.
Vikas Gupta
74
@VikasGupta ¿Qué sugeriría para hacer algo con una colección de pares clave-valor cuando no sabe cuáles serán las claves?
nasch
26
@displayName Si desea hacer algo con cada par clave-valor pero no tiene una referencia a las claves para buscar valores, debe iterar sobre el diccionario, ¿verdad? Solo estaba señalando que podría haber ocasiones en las que quisieras hacer eso, a pesar de la afirmación de Vikas de que este suele ser un uso incorrecto.
nasch
35
Decir que es un uso incorrecto implica que hay una mejor alternativa. ¿Cuál es esa alternativa?
Kyle Delaney
41
VikasGupta está equivocado, puedo afirmar que después de muchos años de programación C # y C ++ de alto rendimiento en escenarios no teóricos. De hecho, hay casos frecuentes en los que uno crearía un diccionario, almacenaría pares clave-valor únicos y luego iteraría sobre estos valores, que han demostrado tener claves únicas dentro de la colección. Crear cualquier colección adicional es una forma realmente ineficiente y costosa de evitar la iteración del diccionario. Proporcione una buena alternativa como respuesta a la pregunta para aclarar su punto de vista, de lo contrario su comentario no tiene sentido.
sɐunıɔ ןɐ qɐp

Respuestas:

3715
foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}
Pablo Fernández
fuente
29
¿Qué pasa si no sé exactamente el tipo de clave / valor en Diccionario? Usar var entryes mejor en ese caso, y así voté esta respuesta en una segunda mirada en lugar de la anterior.
Ozair Kafray el
93
@OzairKafray usar varcuando no sabes el tipo es generalmente una mala práctica.
Nate
52
Esta respuesta es superior porque Pablo no usó de manera predeterminada el uso "var" del codificador perezoso que ofusca el tipo de retorno.
MonkeyWrench
119
@MonkeyWrench: Meh. Visual Studio sabe cuál es el tipo; todo lo que tienes que hacer es pasar el cursor sobre la variable para averiguarlo.
Robert Harvey
31
Según tengo entendido, varsolo 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.
Kyle Delaney
866

Si está intentando usar un Diccionario genérico en C # como usaría una matriz asociativa en otro idioma:

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

O, si solo necesita iterar sobre la colección de claves, use

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

Y, por último, si solo le interesan los valores:

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

(Tenga en cuenta que la varpalabra clave es una característica opcional C # 3.0 y superior, también puede usar el tipo exacto de sus claves / valores aquí)

Jacob
fuente
11
la función var es más necesaria para su primer bloque de código :)
nawfal
27
Aprecio que esta respuesta indique que puede iterar sobre las claves o los valores explícitamente.
Rotsiser Mho
11
No me gusta el uso de var aquí. Dado que es solo azúcar sintáctico, ¿por qué usarlo aquí? Cuando alguien intenta leer el código, tendrá que saltar el código para determinar el tipo de myDictionary(a menos que ese sea el nombre real, por supuesto). Creo que usando var es buena cuando el tipo es evidente, por ejemplo var x = "some string", pero cuando no es inmediatamente obvio creo que es perezosa de codificación que perjudica el lector de código / revisor
James Wierzba
8
vardebería usarse con moderación, en mi opinión. Particularmente aquí, no es constructivo: el tipo KeyValuePaires probablemente relevante para la pregunta.
Sinjai
3
vartiene un propósito único y no creo que sea azúcar 'sintáctico'. Usarlo a propósito es un enfoque apropiado.
Joshua K
159

En algunos casos, puede necesitar un contador que puede proporcionar la implementación for-loop. Para eso, LINQ proporciona lo ElementAtque permite lo siguiente:

for (int index = 0; index < dictionary.Count; index++) {
  var item = dictionary.ElementAt(index);
  var itemKey = item.Key;
  var itemValue = item.Value;
}
Maurício Fedatto
fuente
21
Para usar el método '.ElementAt', recuerde: usar System.Linq; Esto no está incluido en fx. clases de prueba autogeneradas.
Tinia
14
Este es el camino a seguir si está modificando los valores asociados con las teclas. De lo contrario, se produce una excepción al modificar y usar foreach ().
Mike de Klerk
27
¿No es ElementAtuna operación O (n)?
Arturo Torres Sánchez
162
Esta respuesta no merece por completo tantos votos a favor. Un diccionario no tiene un orden implícito, por lo que usarlo .ElementAten este contexto puede generar errores sutiles. Mucho más serio es el punto anterior de Arturo. Estará iterando los dictionary.Count + 1tiempos 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 su dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})lugar y no usarlo .ElementAtdentro del bucle.
gastador
55
ElementAt - o (n) operación! ¿Seriamente? Este es el ejemplo de cómo no debes hacerlo. ¿Cuántos votos a favor?
Mukesh Adhvaryu
96

Depende de si buscas las claves o los valores ...

De la Dictionary(TKey, TValue)descripción de la clase 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);
}
J Healy
fuente
82

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

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

Si solo necesita el valor (permite llamarlo item, más legible que kvp.Value).

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

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:

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

De nuevo, es posible que solo necesite el valor. LINQ también proporciona una solución concisa para:

  • iterar directamente sobre el valor (permite llamarlo item, más legible que kvp.Value)
  • pero ordenado por las llaves

Aquí está:

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

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)!

Stéphane Gourichon
fuente
La última debe ser .Valuesy no una cláusula select.
Mafii
@Mafii ¿Estás seguro? Los valores devueltos por OrderBy no son del tipo KeyValuePair, no tienen Valuecampo. El tipo exacto que veo aquí es IOrderedEnumerable<KeyValuePair<TKey, TValue>>. ¿Quizás quisiste decir algo más? ¿Puedes escribir una línea completa que muestre lo que quieres decir (y probarlo)?
Stéphane Gourichon el
Creo que esta respuesta contiene lo que quiero decir: stackoverflow.com/a/141105/5962841 pero
corrígeme
1
@Mafii Vuelva a leer toda mi respuesta, las explicaciones entre las secciones del código indican el contexto. La respuesta que menciona es como la segunda sección de código en mi respuesta (no se requiere pedido). Ahí acabo de escribir items.Valuecomo sugeriste. En el caso de la cuarta sección que comentó, esta Select()es una forma foreachde enumerar directamente los valores en el diccionario en lugar de los pares clave-valor. Si de alguna manera no le gusta Select()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.
Stéphane Gourichon
1
Si lo hace .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".
Stéphane Gourichon el
53

Diría que foreach es la forma estándar, aunque obviamente depende de lo que estés buscando

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

¿Es eso lo que estás buscando?

George Mauer
fuente
42
¿No es confuso nombrar el "valor" del artículo? Normalmente usaría una sintaxis como "value.Key" y "value.Value", que no es muy intuitiva para cualquier otra persona que lea el código, especialmente si no están familiarizados con cómo se implementa el Diccionario .Net .
RenniePet
3
@RenniePet kvpse utiliza comúnmente para nombrar casos KeyValuePair cuando iterar sobre los diccionarios y estructuras de datos relacionados: foreach(var kvp in myDictionary){....
mbx
37

También puede probar esto en grandes diccionarios para el procesamiento multiproceso.

dictionary
.AsParallel()
.ForAll(pair => 
{ 
    // Process pair.Key and pair.Value here
});
Onur
fuente
8
@WiiMaxx y más importante si estos elementos NO dependen unos de otros
Mafii
37

C # 7.0 introdujo Deconstructors y si está utilizando la aplicación .NET Core 2.0+ , la estructuraKeyValuePair<>ya incluye unaDeconstruct()para usted. Entonces puedes hacer:

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

ingrese la descripción de la imagen aquí

Jaider
fuente
55
Si está utilizando .NET Framework, que al menos hasta 4.7.2 no tiene el Deconstruct en KeyValuePair, intente esto:foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
nwsmith
29

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.

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

    return "Normal";
}

É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.

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

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.

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

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 ?

  • Evite iterar sobre un diccionario si puede, son sustancialmente más lentos que iterar sobre una matriz con los mismos datos.
  • Si elige iterar sobre un diccionario, no intente ser demasiado inteligente, aunque más lento podría hacer mucho peor que usar el método foreach estándar.
Liath
fuente
11
Debe medir con algo como StopWatch en lugar de DateTime: hanselman.com/blog/…
Incluso Mien
2
¿podría describir el escenario de prueba, el número de elementos, donde en su diccionario, ¿con qué frecuencia se ejecuta el escenario para calcular el tiempo medio, ...
WiiMaxx
3
Curiosamente, obtendrá diferentes resultados dependiendo de los datos que tenga en el diccionario. Mientras se itera sobre el Diccionario, la función Enumerador tiene que omitir muchos espacios vacíos en el diccionario, lo que hace que sea más lento que iterar sobre una matriz. Si el Diccionario está lleno, habrá menos espacios vacíos para saltar que si está medio vacío.
Martin Brown
26

Hay muchas opciones Mi favorito personal es KeyValuePair

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

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

También puede usar las Colecciones de claves y valores

theo
fuente
14

Con .NET Framework 4.7uno se puede usar descomposición

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

Para que este código funcione en versiones inferiores de C #, agregue System.ValueTuple NuGet packagey escriba en alguna parte

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;
    }
}
Pavel
fuente
99
Esto es incorrecto. .NET 4.7 simplemente se ha ValueTupleincorporado. Está disponible como un paquete nuget para versiones anteriores. Más importante aún, se necesita C # 7.0+ para que el Deconstructmétodo funcione como deconstructor var (fruit, number) in fruits.
David Arno
13

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:

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

Iterar sobre cualquiera Dictionary<TKey, TVal>de la siguiente manera

// 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}");
}
Domn Werner
fuente
10

Sugeriste a continuación para iterar

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

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

FYI, foreachno funciona si los valores son de tipo objeto.

George Stocker
fuente
44
Por favor explique: foreach¿no funcionará si qué valor es de tipo object? De lo contrario, esto no tiene mucho sentido.
Marc L.
9

La forma más simple de iterar un diccionario:

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

Con C # 7 , agregue este método de extensión a cualquier proyecto de su solución:

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


Y usa esta sintaxis simple

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


O este, si lo prefieres

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


En lugar de lo tradicional

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

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


El método de extensión transforma el KeyValuePairde su IDictionary<TKey, TValue>en un tipo fuerte tuple, 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 a tuples, 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 tupleen comparación con el uso KeyValuePairdirecto, lo cual NO debería ser un problema si está asignando las KeyValuePairpropiedades de 's' Keyy Valuea 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

sɐunıɔ ןɐ qɐp
fuente
1
¿Cuál sería la razón para preferir las tuplas 'cómodas' a las de pares clave-valor? No veo una ganancia aquí. Su tupla contiene una clave y un valor, al igual que el par clave-valor.
Maarten
44
Hola Maarten, gracias por tu pregunta. El principal beneficio es la legibilidad del código sin un esfuerzo adicional de programación. Con KeyValuePair siempre se debe usar el formulario kvp.Keyy kvp.Valuepara 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 como factoryName, y el valor como models, 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ɐunıɔ ןɐ qɐp
7

Sé que esta es una pregunta muy antigua, pero creé algunos métodos de extensión que podrían ser útiles:

    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 esta manera puedo escribir código como este:

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););
boca
fuente
6

A veces, si solo necesita enumerar los valores, use la colección de valores del diccionario:

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

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

ender
fuente
+1 para mejorar el rendimiento. De hecho, iterar sobre el diccionario en sí incluye algunos gastos generales si todo lo que necesita son valores. Esto se debe principalmente a la copia de valores o referencias en estructuras KeyValuePair.
Jaanus Varus
1
Para su información, la última prueba en ese enlace es probar lo incorrecto: mide iterar sobre las claves, sin tener en cuenta los valores, mientras que las dos pruebas anteriores sí usan el valor.
Crescent Fresh
5

Encontré este método en la documentación de la clase DictionaryBase en MSDN:

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

Este fue el único que pude hacer funcionar correctamente en una clase que heredó de DictionaryBase.


fuente
3
Esto se parece a cuando se usa la versión no genérica de Dictionary ... es decir, antes de .NET Framework 2.0.
joedotnot
@ joed0tnot: es la versión no Hashtable
genérica
5

foreaches más rápido y si solo repite ___.Values, también es más rápido

ingrese la descripción de la imagen aquí

Pixel_95
fuente
1
¿Por qué estás llamando ContainsKey()en la forversió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, si dictcontiene un rango contiguo de enteros de 0a dictCount - 1, usted sabe que el indexador no puede fallar; de lo contrario, dict.Keyses lo que debería estar iterando. De cualquier manera, no ContainsKey()/ TryGetValue()necesario. Por último, no publique capturas de pantalla de código.
BACON
3

Aprovecharé .NET 4.0+ y proporcionaré una respuesta actualizada a la aceptada originalmente:

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

La forma estándar de iterar sobre un diccionario, de acuerdo con la documentación oficial en MSDN es:

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

Escribí una extensión para recorrer un diccionario.

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

Entonces puedes llamar

myDictionary.ForEach((x,y) => Console.WriteLine(x + " - " + y));
Steven Delrue
fuente
Definiste un ForEachmétodo en el que tienes foreach (...) { }... Parece innecesario.
Maarten
1

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.

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

fuente
1

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 KeyValuePairagnóstica:

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

// ...

foreach (var (key, value) in dictionary)
{
    // ...
}
rucamzu
fuente
0
var dictionary = new Dictionary<string, int>
{
    { "Key", 12 }
};

var aggregateObjectCollection = dictionary.Select(
    entry => new AggregateObject(entry.Key, entry.Value));
Pixar
fuente
2
Es necesario que haya más justificación / descripción en esta respuesta. ¿A qué se AggregateObjectagrega KeyValuePair? ¿Dónde está la "iteración", como se solicita en la pregunta?
Marc L.
Seleccionar itera sobre el diccionario y nos permite trabajar en cada objeto. No es tan general como foreach, pero lo he usado mucho. ¿Mi respuesta realmente merecía un voto negativo?
Pixar
No, Select usa la iteración para efectuar el resultado, pero no es un iterador en sí mismo. Los tipos de cosas para los que foreachse usa la iteración ( ), especialmente las operaciones con efectos secundarios, están fuera del alcance de Linq, incluido Select. La lambda no se ejecutará hasta que aggregateObjectCollectionse enumere realmente. Si esta respuesta se toma como un "primer camino" (es decir, se usa antes de una recta foreach), 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.
Marc L.
0

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:

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

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 ().

var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );
Alex
fuente
55
Copiar la colección completa sin ningún motivo no mejorará el rendimiento. Reducirá drásticamente el código y duplicará la huella de memoria del código que no debería consumir prácticamente memoria adicional.
Servy
Evito el método de efectos secundarios 'List.ForEach': foreachfuerza la visibilidad de los efectos secundarios hacia arriba, donde pertenece.
usuario2864740
0

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.

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);
}
Sheo Dayal Singh
fuente
0

Si desea usar for loop, puede hacer esto:

var keyList=new List<string>(dictionary.Keys);
for (int i = 0; i < keyList.Count; i++)
{
   var key= keyList[i];
   var value = dictionary[key];
 }
Seçkin Durgay
fuente
Sin embargo, ¿cuál es el beneficio de esto? Es un código más largo que un foreachbucle y un rendimiento peor porque new List<string>(dictionary.Keys)repetirá dictionary.Countveces 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 un forbucle".
BACON
Si tiene una colección grande y las operaciones son lentas en foreach y si su colección puede cambiar cuando itera, lo protege del error "la colección ha cambiado" y esta solución tiene un mejor rendimiento que el uso de ElementAt.
Seçkin Durgay
Estoy de acuerdo en que evitar las excepciones de "Se modificó la colección" es una razón para hacerlo, aunque ese caso especial no se mencionó en la pregunta y siempre se podría hacer 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.
BACON
sí, tienes razón, pero hay una respuesta aquí con ElementAt y tiene una reputación muy alta y
escribí
-2

simple con 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}") );
Luiz Fernando Corrêa Leite
fuente
Veo que estás llamando ToList()porque ForEach()solo se define en la List<>clase, pero ¿por qué hacer todo eso en lugar de solo foreach (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.
TOCINO
Solo por la visualización del código, realmente podría hacerse de la manera que usted indicó, solo quería presentarlo de una manera diferente y, desde mi punto de vista, me disculpo por no ver la respuesta indicada, un abrazo
Luiz Fernando Corrêa Leite
-4

Además de las publicaciones de más alto rango donde hay una discusión entre el uso

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

o

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

el más completo es el siguiente porque puede ver el tipo de diccionario desde la inicialización, kvp es 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
}
Gran jefe
fuente
55
Crear y copiar un segundo diccionario no es una solución válida para la legibilidad del código. De hecho, diría que haría que el código sea más difícil de entender porque ahora tienes que preguntarte: "¿Por qué el último chico creó un segundo diccionario?" Si quieres ser más detallado, solo usa la opción uno.
Matthew Goulart el
Solo quería mostrarte que cuando decidas antes para cada uno, el uso en el foreach está claro en la declaración
BigChief