c# recorrer - ¿Cuál es la mejor manera de iterar sobre un diccionario?




13 Answers

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

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




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

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



En general, pedir "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, 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, del gusto.

Manera más sencilla

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

Una vez más, es posible que sólo necesite el valor. LINQ también proporciona una solución concisa para:

  • iterar directamente en el valor (permite llamarlo item , más legible que kvp.Value )
  • pero ordenados 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 a partir de estos ejemplos. Si no necesita una orden específica, simplemente siga la "manera más directa" (ver arriba).




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

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



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 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 la misma cantidad de elementos) tomó 0.0365073 segundos, ¡lo cual es 2.4 veces más largo! Aunque he visto diferencias mucho mayores. Para comparación, una lista estaba en algún punto intermedio entre 0.00215043 segundos.

Sin embargo, eso es como comparar manzanas y naranjas. Mi punto es que la iteración sobre los diccionarios es lenta.

Los diccionarios están optimizados para búsquedas, por lo 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";
}

Este carga las claves y las itera sobre ellas (también intenté jalar 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 normal de foreach tomó 0.0310062 y la versión de las llaves 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 al usar las teclas aquí (en este punto, solo tenía curiosidad):

Aquí está el método RunTest si eso te ayuda a visualizar lo que está pasando.

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

En este caso, la ejecución normal de foreach tomó 0.2820564 segundos (aproximadamente diez veces más de lo que tomó una iteración, como era de esperar). La iteración sobre las teclas tomó 2.2249449 segundos.

Editado para agregar: Al leer algunas de las otras respuestas, me pregunté qué pasaría si utilizara Diccionario en lugar de Diccionario. En este ejemplo, la matriz tomó 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 a la lentitud del 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 en él.
  • Si elige iterar sobre un diccionario, no intente ser demasiado inteligente, aunque más lento podría hacerlo mucho peor que usar el método estándar para cada método.



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

Para su información, foreach no funciona si el valor es de tipo objeto.







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 obtener funcionando correctamente en una clase que heredó de DictionaryBase.




La forma estándar de iterar sobre un Diccionario, según la documentación oficial en MSDN es:

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



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 en KeyValuePair<TKey, TVal> que lo KeyValuePair<TKey, TVal> :

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

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



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

Si esto agrega una llamada adicional de '.ToList ()', puede haber una leve mejora en el rendimiento (como se indica aquí para cada vs vs. Lista.Para cada () {} ), especialmente cuando se trabaja con grandes diccionarios y se ejecuta en paralelo no Opción / no tendrá ningún efecto en absoluto.

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



Dictionary <TKey, TValue> Es una clase de colección genérica en c # y almacena los datos en el formato del valor 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 tratado 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);
}



Los diccionarios son listas especiales, mientras que cada valor en la lista tiene una clave que también es una variable. Un buen ejemplo de un diccionario es una guía telefónica.

   Dictionary<string, long> phonebook = new Dictionary<string, long>();
    phonebook.Add("Alex", 4154346543);
    phonebook["Jessica"] = 4159484588;

Tenga en cuenta que al definir un diccionario, debemos proporcionar una definición genérica con dos tipos: el tipo de clave y el tipo de valor. En este caso, la clave es una cadena, mientras que el valor es un número entero.

También hay dos formas de agregar un solo valor al diccionario, ya sea usando el operador de corchetes o usando el método Agregar.

Para verificar si un diccionario tiene cierta clave, podemos usar el método ContainsKey:

Dictionary<string, long> phonebook = new Dictionary<string, long>();
phonebook.Add("Alex", 415434543);
phonebook["Jessica"] = 415984588;

if (phonebook.ContainsKey("Alex"))
{
    Console.WriteLine("Alex's number is " + phonebook["Alex"]);
}

Para eliminar un elemento de un diccionario, podemos utilizar el método Eliminar. Eliminar un elemento de un diccionario por su clave es rápido y muy eficiente. Al eliminar un elemento de una Lista utilizando su valor, el proceso es lento e ineficiente, a diferencia de la función Eliminar del diccionario.

Dictionary<string, long> phonebook = new Dictionary<string, long>();
phonebook.Add("Alex", 415434543);
phonebook["Jessica"] = 415984588;

phonebook.Remove("Jessica");
Console.WriteLine(phonebook.Count);



Related