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


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


1944
2017-09-26 18:20


origen


Respuestas:


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

2892
2017-09-26 18:22



Si está tratando de usar un diccionario genérico en C # como si fuera una matriz asociativa en otro idioma:

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

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

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

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

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

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


643
2017-09-26 18:22



En algunos casos, es posible que necesite un contador que puede ser proporcionado por la implementación for-loop. Para eso, LINQ proporciona ElementAt que permite lo siguiente:

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

107
2018-03-10 20:44



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

Desde el MSDN Dictionary(TKey, TValue) Descripción de la clase:

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

74
2017-09-26 18:27



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 maneras de iterar sobre un diccionario en C # y no hay una 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 necesitas 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 por el 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 teclas

Aquí está:

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

Hay muchos más casos de uso en el mundo real que puede hacer a partir de estos ejemplos. Si no necesita un pedido específico, simplemente manténgase en la "forma más directa" (ver arriba).


55
2017-08-10 11:15



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?


36
2017-09-26 18:22



También puedes probar esto en diccionarios grandes para procesar multiproceso.

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

27
2018-06-11 13:32



Hay muchas opciones. Mi favorito personal es por 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


22
2017-09-26 18:22



Aprecio que esta pregunta ya haya tenido muchas respuestas, pero quería hacer un poco de investigación.

Iterar sobre un diccionario puede ser bastante lento cuando se compara con iterar sobre algo así 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, ¡es 2.4 veces más largo! Aunque he visto diferencias mucho mayores. Para comparar, 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 búsquedas, por lo que 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 itera en su lugar (también intenté tirar las llaves 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, he realizado mi iteración diez veces para ver si hay algún beneficio con el uso de las teclas aquí (en este momento solo 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 tomó 0.2820564 segundos (alrededor de diez veces más de lo que requería una única 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 usaba 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 sobre cuánto 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 eliges iterar sobre un diccionario, no intentes ser demasiado inteligente, aunque más lento podrías hacer mucho peor que usar el método foreach estándar.

22
2017-07-30 10:54



Usted sugirió 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, foreach no funciona si el valor es de tipo objeto


9
2017-10-28 20:49



Con .NET Framework 4.7 uno 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 de C # inferiores, agregue System.ValueTuple NuGet package y escribe 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;
    }
}

8
2017-10-17 15:18