He visto algunas formas diferentes de iterar sobre un diccionario en C #. ¿Hay una forma estándar?
He visto algunas formas diferentes de iterar sobre un diccionario en C #. ¿Hay una forma estándar?
Respuestas:
foreach(KeyValuePair<string, string> entry in myDictionary)
{
// do something with entry.Value or entry.Key
}
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:
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 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í)
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
var
debería usarse con moderación, en mi opinión. Particularmente aquí, no es constructivo: el tipo KeyValuePair
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:
for (int index = 0; index < dictionary.Count; index++) {
var item = dictionary.ElementAt(index);
var itemKey = item.Key;
var itemValue = item.Value;
}
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 los dictionary.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 su dictionary.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 :
// 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);
}
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.
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)
}
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:
item
, más legible que kvp.Value
)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)!
.Values
y no una cláusula select.
Value
campo. 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)?
items.Value
como sugeriste. En el caso de la cuarta sección que comentó, esta Select()
es una forma foreach
de 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.
.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
foreach(var kvp in my_dictionary) {
...
}
¿Es eso lo que estás buscando?
kvp
se utiliza comúnmente para nombrar casos KeyValuePair cuando iterar sobre los diccionarios y estructuras de datos relacionados: foreach(var kvp in myDictionary){...
.
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}");
}
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.
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 ?
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
Con .NET Framework 4.7
uno 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 package
y 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;
}
}
ValueTuple
incorporado. Está disponible como un paquete nuget para versiones anteriores. Más importante aún, se necesita C # 7.0+ para que el Deconstruct
método funcione como deconstructor var (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:
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}");
}
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, foreach
no funciona si los valores son de tipo objeto.
foreach
¿no funcionará si qué valor es de tipo object
? De lo contrario, esto no tiene mucho sentido.
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 KeyValuePair
de 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 tuple
en comparación con el uso KeyValuePair
directo, lo cual NO debería ser un problema si está asignando las KeyValuePair
propiedades de 's' Key
y Value
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
kvp.Key
y kvp.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 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é 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););
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
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.
Hashtable
ContainsKey()
en la for
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, si dict
contiene un rango contiguo de enteros de 0
a dictCount - 1
, usted sabe que el indexador no puede fallar; de lo contrario, dict.Keys
es lo que debería estar iterando. De cualquier manera, no ContainsKey()
/ TryGetValue()
necesario. Por último, no publique capturas de pantalla de código.
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));
ForEach
método en el que tienes foreach (...) { }
... 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.
public new IEnumerator<T> GetEnumerator()
{
return this.Values.GetEnumerator();
}
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:
var dictionary = new Dictionary<int, string>();
// ...
foreach (var (key, value) in dictionary)
{
// ...
}
var dictionary = new Dictionary<string, int>
{
{ "Key", 12 }
};
var aggregateObjectCollection = dictionary.Select(
entry => new AggregateObject(entry.Key, entry.Value));
AggregateObject
agrega KeyValuePair
? ¿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 que foreach
se 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 aggregateObjectCollection
se 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.
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 } );
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.
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);
}
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];
}
foreach
bucle y un rendimiento peor porque new 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 un for
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
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}") );
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.
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
}