LINQ con groupby y count

221

Esto es bastante simple pero estoy perdido: dado este tipo de conjunto de datos:

UserInfo(name, metric, day, other_metric)

y este conjunto de datos de muestra:

joe  1 01/01/2011 5
jane 0 01/02/2011 9
john 2 01/03/2011 0
jim  3 01/04/2011 1
jean 1 01/05/2011 3
jill 2 01/06/2011 5
jeb  0 01/07/2011 3
jenn 0 01/08/2011 7

Me gustaría recuperar una tabla que enumera las métricas en orden (0,1,2,3 ..) con el número total de veces que ocurre el conteo. Entonces de este conjunto terminarías con:

0 3    
1 2    
2 2    
3 1

Estoy lidiando con la sintaxis de LINQ pero estoy atascado en dónde poner un groupby y contar ... ¿alguna ayuda?

POST Edit: nunca pude hacer que las respuestas publicadas funcionen, ya que siempre devolvieron un registro con el número de recuentos diferentes. Sin embargo, pude armar un ejemplo de LINQ to SQL que funcionó:

        var pl = from r in info
                 orderby r.metric    
                 group r by r.metric into grp
                 select new { key = grp.Key, cnt = grp.Count()};

Este resultado me dio un conjunto ordenado de registros con 'métricas' y el número de usuarios asociados con cada uno. Soy claramente nuevo en LINQ en general y, para mi ojo inexperto, este enfoque parece muy similar al enfoque puro de LINQ, pero me dio una respuesta diferente.

Gio
fuente
Sí, pero la explicación de Jimmy me ayudó más. Sin embargo, nunca pude hacer que su ejemplo funcionara, pero me llevó a una nueva dirección.
Gio
@Jimmy usó la sintaxis funcional para expresiones LINQ en lugar de la sintaxis de consulta LINQ estándar, además decidió mostrar la ejecución inmediata de esas funciones en lugar del formato de ejecución retrasada. Para una nueva persona eso sería confuso. No sé por qué hizo eso.
Richard Robertson

Respuestas:

395

Después de llamar GroupBy, obtienes una serie de grupos IEnumerable<Grouping>, donde cada Agrupación misma expone lo Keyusado para crear el grupo y también es unIEnumerable<T> de los elementos que están en tu conjunto de datos original. Solo tiene que recurrir Count()a esa Agrupación para obtener el subtotal.

foreach(var line in data.GroupBy(info => info.metric)
                        .Select(group => new { 
                             Metric = group.Key, 
                             Count = group.Count() 
                        })
                        .OrderBy(x => x.Metric)
{
     Console.WriteLine("{0} {1}", line.Metric, line.Count);
}


Esta fue una respuesta brillante y rápida, pero tengo un pequeño problema con la primera línea, específicamente "data.groupby (info => info.metric)"

Supongo que ya tiene una lista / matriz de algunos classque se parece

class UserInfo {
    string name;
    int metric;
    ..etc..
} 
...
List<UserInfo> data = ..... ;

Cuando lo haga data.GroupBy(x => x.metric), significa "para cada elemento xen el IEnumerable definido por data, calcule .metric, luego agrupe todos los elementos con la misma métrica en ay Groupingdevuelva uno IEnumerablede todos los grupos resultantes. Dado su conjunto de datos de ejemplo de

    <DATA>           | Grouping Key (x=>x.metric) |
joe  1 01/01/2011 5  | 1
jane 0 01/02/2011 9  | 0
john 2 01/03/2011 0  | 2
jim  3 01/04/2011 1  | 3
jean 1 01/05/2011 3  | 1
jill 2 01/06/2011 5  | 2
jeb  0 01/07/2011 3  | 0
jenn 0 01/08/2011 7  | 0

daría como resultado el siguiente resultado después de groupby:

(Group 1): [joe  1 01/01/2011 5, jean 1 01/05/2011 3]
(Group 0): [jane 0 01/02/2011 9, jeb  0 01/07/2011 3, jenn 0 01/08/2011 7]
(Group 2): [john 2 01/03/2011 0, jill 2 01/06/2011 5]
(Group 3): [jim  3 01/04/2011 1]
Palanqueta
fuente
Esta fue una respuesta brillante y rápida, pero tengo un pequeño problema con la primera línea, específicamente "data.groupby (info => info.metric)". Claramente, 'datos' es el conjunto de datos actual, pero ¿qué representa 'info.metric'? la definición de clase?
Gio
"info.metric" sería la propiedad / campo métrico en la clase UserInfo que mencionó en su pregunta.
lee-m
1
Gracias lo descubrí, pero en realidad esto parece darme un valor único: el número total de diferentes recuentos métricos. En este ejemplo obtengo "métrica 4" que me indica cuántos recuentos diferentes tengo.
Gio
1
Guau. ¡Explicaste totalmente la agrupación! Solo eso valió la publicación ... todavía obtengo el resultado de "métricas 4", pero gracias.
Gio
44
El comienzo de esta respuesta, "después de llamar a GroupBy, obtienes una serie de grupos IEnumerable <Grouping>, donde cada Agrupación expone la Clave utilizada para crear el grupo y también es un IEnumerable <T> de cualquier elemento que esté en tus datos originales set ", es la explicación más clara de LINQ GroupBy todavía he leído, gracias.
dumbledad
48

Suponiendo que userInfoListes un List<UserInfo>:

        var groups = userInfoList
            .GroupBy(n => n.metric)
            .Select(n => new
            {
                MetricName = n.Key,
                MetricCount = n.Count()
            }
            )
            .OrderBy(n => n.MetricName);

La función lambda para GroupBy(), n => n.metricsignifica que va a conseguir ámbito metricde cada UserInfoobjeto encontrado. El tipo de ndepende del contexto, en la primera aparición es de tipo UserInfo, porque la lista contiene UserInfoobjetos. En el segundo caso nes de tipo Grouping, porque ahora es una lista de Groupingobjetos.

Groupings tienen métodos de extensión como .Count(), .Key()y casi cualquier otra cosa que esperarías. Del mismo modo que verificaría .Lenghten un string, puede verificar .Count()un grupo.

Vladislav Zorov
fuente
23
userInfos.GroupBy(userInfo => userInfo.metric)
        .OrderBy(group => group.Key)
        .Select(group => Tuple.Create(group.Key, group.Count()));
DanielOfTaebl
fuente
1
pequeño error tipográfico -> group.keyen el Select (...) debe sergroup.Key
Jan