Lo primero que debe tener en cuenta es que en realidad no le importan las teclas del diccionario. El primer paso, por lo tanto, es ignorarlos como irrelevantes para la tarea en cuestión. Vamos a trabajar con la propiedad Values
del diccionario, y el trabajo es muy similar al de cualquier otra colección de enteros (o cualquier otro enumerable de cualquier otro tipo que podamos comparar para la igualdad).
Existen dos enfoques comunes para este problema, que vale la pena conocer.
El primero utiliza otro diccionario para mantener el recuento de valores:
//Start with setting up the dictionary you described.
Dictionary<string, int> dict = new Dictionary<string, int>{
{"key1", 2},
{"key2", 2},
{"key3", 3},
{"key4", 2},
{"key5", 5},
{"key6", 5}
};
//Create a different dictionary to store the counts.
Dictionary<int, int> valCount = new Dictionary<int, int>();
//Iterate through the values, setting count to 1 or incrementing current count.
foreach(int i in dict.Values)
if(valCount.ContainsKey(i))
valCount[i]++;
else
valCount[i] = 1;
//Finally some code to output this and prove it worked:
foreach(KeyValuePair<int, int> kvp in valCount)//note - not sorted, that must be added if needed
Console.WriteLine("{0} - {1}", kvp.Key, kvp.Value);
Esperemos que esto es bastante sencillo. Otro enfoque es más complicado, pero tiene algunas ventajas:
//Start with setting up the dictionary you described.
Dictionary<string, int> dict = new Dictionary<string, int>{
{"key1", 2},
{"key2", 2},
{"key3", 3},
{"key4", 2},
{"key5", 5},
{"key6", 5}
};
IEnumerable<IGrouping<int, int>> grp = dict.Values.GroupBy(x => x);
//Two options now. One is to use the results directly such as with the
//equivalent code to output this and prove it worked:
foreach(IGrouping<int, int> item in grp)//note - not sorted, that must be added if needed
Console.WriteLine("{0} - {1}", item.Key, item.Count());
//Alternatively, we can put these results into another collection for later use:
Dictionary<int, int> valCount = grp.ToDictionary(g => g.Key, g => g.Count());
//Finally some code to output this and prove it worked:
foreach(KeyValuePair<int, int> kvp in valCount)//note - not sorted, that must be added if needed
Console.WriteLine("{0} - {1}", kvp.Key, kvp.Value);
(probablemente Usaríamos var
en lugar de la verbosa IEnumerable<IGrouping<int, int>>
, pero vale la pena ser preciso al explicar el código).
En una comparación directa, esta versión es inferior, más complicada de entender y menos eficiente. Sin embargo, aprender este enfoque permite algunas variantes concisas y eficientes de la misma técnica, por lo que vale la pena examinarla.
GroupBy()
toma una enumeración y crea otra enumeración que contiene pares clave-valor donde el valor también es una enumeración. La lambda x => x
significa que está agrupada por sí misma, pero tenemos la flexibilidad para diferentes reglas de agrupamiento. El contenido de grp
se ve un poco como:
{
{Key=2, {2, 2, 2}}
{Key=3, {3}}
{Key=5, {5, 5}}
}
lo tanto, si nos bucle a través de esta una para cada grupo sacamos la Key
y llamamos Count()
en el grupo, obtenemos los resultados que queremos.
Ahora, en el primer caso construimos nuestro conteo en un solo pase O (n), mientras que aquí construimos el grupo en un pase O (n), y luego obtenemos el conteo en un segundo O (n) pasar, por lo que es mucho menos eficiente. También es un poco más difícil de entender, ¿por qué molestarse en mencionarlo?
Bueno, lo primero es que una vez que entendemos que podemos convertir las líneas:
IEnumerable<IGrouping<int, int>> grp = dict.Values.GroupBy(x => x);
foreach(IGrouping<int, int> item in grp)
Console.WriteLine("{0} - {1}", item.Key, item.Count());
En:
foreach(var item in dict.Values.GroupBy(x => x))
Console.WriteLine("{0} - {1}", item.Key, item.Count());
que es bastante conciso, y se convierte en idiomática.Es especialmente bueno si queremos continuar y hacer algo más complicado con los pares de recuento de valores, ya que podemos encadenar esto en otra operación.
La versión que pone los resultados en un diccionario puede ser aún más concisa sigue:
var valCount = dict.Values.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
Allí, toda respuesta a su pregunta en una línea corta, en lugar de los 6 (que cortan los comentarios) para la primera versión.
(Algunos pueden preferir reemplazar dict.Values.GroupBy(x => x)
con dict.GroupBy(x => x.Value)
que tendrá exactamente los mismos resultados una vez que se corre el Count()
en él. Si no está seguro de por qué inmediatamente, trate de resolverlo).
La otra ventaja, es que tenemos más flexibilidad con GroupBy
en otros casos. Por estas razones, es probable que las personas que están acostumbradas a usar GroupBy
comiencen con la concordancia de una línea de dict.Values.GroupBy(x => x).ToDictinary(g => g.Key, g => g.Count());
y luego cambien a la forma más detallada pero más efciente de la primera versión (donde incrementamos los totales acumulados en el nuevo diccionario) si resultó ser un punto de acceso de rendimiento.
Un poco más de información: ¿Estás preguntando un recuento de los valores que no se repiten? ¿Podría darnos un ejemplo de datos y su resultado deseado? – Alan
La prueba de dobles para la igualdad es una práctica muy cuestionable. Es posible que desee evitar mencionarlo si quiere una respuesta. El uso de Distinct(). Count() de Linq en la propiedad Values es, de lo contrario, un enfoque que coincide bien con sus etiquetas. –
¿Y cómo quiere probar la igualdad de los dobles aquí? –