294 votes

LINQ avec groupby et count

C'est assez simple mais je suis perdu : Étant donné ce type d'ensemble de données :

UserInfo(name, metric, day, other_metric)

et cet exemple de données :

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

Je voudrais récupérer un tableau qui liste les métriques dans l'ordre (0,1,2,3..) avec le nombre total de fois où le compte apparaît. Donc, à partir de cet ensemble, vous obtiendrez :

0 3    
1 2    
2 2    
3 1

Je me débrouille avec la syntaxe LINQ mais je ne sais pas où mettre un groupby et un count... Une aide ?

POST Edit : Je n'ai jamais pu faire fonctionner les réponses affichées car elles retournaient toujours un enregistrement avec le nombre de comptes différents. Cependant, j'ai pu mettre au point un exemple LINQ to SQL qui a fonctionné :

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

Ce résultat m'a donné un ensemble ordonné d'enregistrements avec des "métriques" et le nombre d'utilisateurs associés à chacun. Je suis clairement novice en matière de LINQ en général et, pour mon œil non averti, cette approche semble très similaire à l'approche LINQ pure, mais elle me donne une réponse différente.

0 votes

Oui, mais l'explication de Jimmy m'a aidé davantage. Je n'ai jamais réussi à faire fonctionner son exemple, mais il m'a conduit dans une nouvelle direction.

0 votes

@Jimmy a utilisé la syntaxe fonctionnelle pour les expressions LINQ plutôt que la syntaxe de requête LINQ standard, et il a décidé de montrer l'exécution immédiate de ces fonctions plutôt que le format d'exécution différée. Pour une personne novice, cela pourrait prêter à confusion. Je ne sais pas pourquoi il a fait cela.

504voto

Jimmy Points 35501

Après avoir appelé GroupBy vous obtenez une série de groupes IEnumerable<Grouping> où chaque Groupement expose lui-même le Key utilisé pour créer le groupe et est également un IEnumerable<T> de tous les éléments qui se trouvent dans votre ensemble de données original. Il vous suffit d'appeler Count() sur ce groupement pour obtenir le sous-total.

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

> Cette réponse a été brillamment rapide mais j'ai un petit problème avec la première ligne, en particulier "data.groupby(info=>info.metric)".

Je suppose que vous avez déjà une liste ou un tableau de quelques class qui ressemble à

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

Quand vous le faites data.GroupBy(x => x.metric) cela signifie "pour chaque élément x dans l'IEnumerable défini par data calculez que c'est .metric puis on regroupe tous les éléments ayant la même métrique dans un groupe Grouping et retourner un IEnumerable de tous les groupes résultants. Étant donné votre exemple d'ensemble de données 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

il en résulterait le résultat suivant après le 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]

0 votes

Cette réponse a été brillamment rapide mais j'ai un petit problème avec la première ligne, en particulier "data.groupby(info=>info.metric)". Il est clair que 'data' est l'ensemble de données actuel, mais que représente 'info.metric' ? la définition de la classe ?

0 votes

"info.metric" serait la propriété/le champ métrique de la classe UserInfo que vous avez mentionnée dans votre question.

1 votes

Merci d'avoir compris, mais en fait, cela semble me donner une seule valeur - à savoir le nombre total de comptages de métriques différentes. Dans cet exemple, j'obtiens "metrics 4", ce qui m'indique le nombre de comptages différents dont je dispose.

82voto

Vladislav Zorov Points 1712

En supposant userInfoList est List<UserInfo>:

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

La fonction lambda pour GroupBy(), n => n.metric signifie qu'il sera le champ metric de chaque UserInfo objet rencontré. Le type d' n est en fonction du contexte, dans la première occurrence, il est de type UserInfo, parce que la liste contient UserInfo objets. Dans la deuxième occurrence n est de type Grouping, parce que maintenant c'est une liste d' Grouping objets.

Groupings ont des méthodes d'extension comme .Count(), .Key() et de n'importe quoi d'autre que vous attendez. Tout comme vous le feriez vérifiez .Lenght sur string, vous pouvez vérifier l' .Count() sur un groupe.

26voto

DanielOfTaebl Points 361
userInfos.GroupBy(userInfo => userInfo.metric)
        .OrderBy(group => group.Key)
        .Select(group => Tuple.Create(group.Key, group.Count()));

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X