1. Intro
La groupification de données, c’est le fait de séparer les éléments d’une collection en “paniers” selon un critère commun. Imagine que tu tries des pommes par couleur : toutes les rouges dans un panier, les vertes dans un autre, les jaunes ailleurs. En code, ça s’appelle regrouper.
Pourquoi faire ça ?
Dans la vraie vie, c’est super utile de regrouper des gens par ville, des produits par catégorie, des transactions par date, etc. Ça permet de faire de l’analytics : genre savoir combien d’étudiants il y a dans chaque classe, ou quelles villes sont les plus étudiantes.
LINQ te file une méthode spéciale pour ça : GroupBy. Et t’as aussi un opérateur équivalent en query syntax — c’est group by. On va voir tout ça avec des exemples sur notre appli d’école — un système pour gérer les étudiants et les classes.
2. GroupBy en method syntax
Signature de base et ce que ça retourne
La méthode GroupBy fait un peu flipper au début :
IEnumerable<IGrouping<TKey, TElement>> GroupBy<TElement, TKey>(
this IEnumerable<TElement> source,
Func<TElement, TKey> keySelector
)
Où :
- source — la collection de base (genre une liste d’étudiants).
- keySelector — une fonction qui dit sur quoi tu regroupes, genre la propriété ClassName ou City.
Important !
Le résultat, c’est pas juste un tableau de tes objets, mais une collection de groupes spéciaux (IGrouping<TKey, TElement>). Chaque groupe contient :
- La clé du groupe (Key — le truc qui a réuni les éléments),
- La collection des éléments qui sont dans ce groupe.
Schéma :
| Clé (Key) | Éléments du groupe (groupe d’objets) |
|---|---|
| "9A" | Ivan, Oleg, Maria |
| "10B" | Svetlana, Piotr |
| ... | ... |
Exemple 1 : On regroupe les étudiants par nom de classe
Imaginons qu’on a une classe Student :
public class Student
{
public string Name { get; set; }
public string ClassName { get; set; }
public int Grade { get; set; }
}
Et une collection d’étudiants :
var students = new List<Student>
{
new Student { Name = "Ivan", ClassName = "9A", Grade = 5 },
new Student { Name = "Oleg", ClassName = "9A", Grade = 4 },
new Student { Name = "Maria", ClassName = "10B", Grade = 5 },
new Student { Name = "Svetlana", ClassName = "10B", Grade = 3 }
};
On regroupe les étudiants par classe :
var studentsByClass = students.GroupBy(s => s.ClassName);
foreach (var group in studentsByClass)
{
Console.WriteLine($"Classe : {group.Key}"); // Clé du groupe
foreach (var student in group)
{
Console.WriteLine($" - {student.Name}, note : {student.Grade}");
}
}
Qu’est-ce qui se passe ?
La méthode GroupBy nous a créé deux groupes : un pour "9A", un autre pour "10B". En parcourant les groupes, on peut afficher n’importe quelle info agrégée sur chaque “tas”.
Visualisation : à quoi ressemble la collection après GroupBy
Tu peux voir ça comme ça :
students.GroupBy(s => s.ClassName)
├─ groupe "9A" { Ivan, Oleg }
└─ groupe "10B" { Maria, Svetlana }
Chaque “branche” — c’est une mini-liste d’étudiants, réunis par la clé.
Étudiants par note : groupification sur n’importe quel critère
Allez, on regroupe pas par classe, mais par note !
var studentsByGrade = students.GroupBy(s => s.Grade);
foreach (var group in studentsByGrade)
{
Console.WriteLine($"Note : {group.Key}");
foreach (var student in group)
{
Console.WriteLine($" - {student.Name} de la classe {student.ClassName}");
}
}
Sortie :
Note : 5
- Ivan de la classe 9A
- Maria de la classe 10B
Note : 4
- Oleg de la classe 9A
Note : 3
- Svetlana de la classe 10B
3. Travailler avec les groupes en LINQ : quoi faire après GroupBy
Souvent, après avoir groupé, on veut pas juste afficher les groupes, mais choper une info agrégée — genre compter combien d’éléments dans chaque groupe, calculer la moyenne, faire une liste de noms, etc.
Exemple 2 : Compter le nombre d’étudiants dans chaque classe
var classCounts = students
.GroupBy(s => s.ClassName)
.Select(g => new { Class = g.Key, Count = g.Count() });
foreach (var cc in classCounts)
{
Console.WriteLine($"Dans la classe {cc.Class} — {cc.Count} étudiants");
}
Résultat :
Dans la classe 9A — 2 étudiants
Dans la classe 10B — 2 étudiants
- D’abord on a groupé les étudiants.
- Puis pour chaque groupe, on a créé un objet anonyme avec la clé (nom de la classe) et le nombre (méthode Count()).
Exemple 3 : Faire la liste des noms des meilleurs élèves par classe
var excellentByClass = students
.Where(s => s.Grade == 5)
.GroupBy(s => s.ClassName)
.Select(g => new
{
Class = g.Key,
ExcellentStudents = g.Select(s => s.Name).ToList()
});
foreach (var group in excellentByClass)
{
Console.WriteLine($"Dans la classe {group.Class} les meilleurs : {string.Join(", ", group.ExcellentStudents)}");
}
4. group by en query syntax
Si tu kiffes le style SQL, ou si tu viens du monde des bases de données, LINQ gère aussi la syntaxe des requêtes genre group by ... into ....
Exemple de base de groupification
var studentsByClass =
from s in students
group s by s.ClassName into classGroup
select classGroup;
foreach (var group in studentsByClass)
{
Console.WriteLine($"Classe : {group.Key}");
foreach (var student in group)
{
Console.WriteLine($" - {student.Name}");
}
}
D’abord on “group by” sur le critère, puis on donne un nom au groupe (into classGroup) — et après tu fais ce que tu veux avec.
Projection avec agrégats
Imaginons qu’on veut savoir les classes et combien d’étudiants dans chaque classe :
var countsByCity =
from s in students
group s by s.ClassName into classGroup
select new { Class = classGroup.Key, Count = classGroup.Count() };
foreach (var item in countsByCity)
{
Console.WriteLine($"Dans la classe {item.Class} — {item.Count} étudiants");
}
— La syntaxe est la même, mais après select tu peux faire n’importe quelle projection (agrégat, liste, moyenne, etc.).
5. Scénarios bonus
Groupification sur plusieurs critères (clé composée)
Parfois tu veux regrouper sur une combinaison de propriétés. Genre par classe ET par note :
var byClassAndGrade = students
.GroupBy(s => new { s.ClassName, s.Grade });
foreach (var group in byClassAndGrade)
{
Console.WriteLine($"Classe : {group.Key.ClassName}, Note : {group.Key.Grade}");
foreach (var student in group)
{
Console.WriteLine($" - {student.Name}");
}
}
La clé du groupe maintenant — c’est un type anonyme (combinaison de plusieurs champs).
Comment ça marche les groupes en vrai ?
Un peu de cuisine interne LINQ
- Quand tu fais GroupBy, LINQ construit une “table” interne : pour chaque valeur unique de clé — un groupe séparé.
- Les groupes implémentent l’interface IGrouping<TKey, TElement>. Tu peux accéder à la propriété Key pour choper la valeur du critère de groupification.
Lazy evaluation et perf
- Les groupes sont calculés à la volée, seulement quand tu commences à les parcourir (foreach). Donc, même si t’as des grosses collections — t’inquiète pas, les groupes sont créés que quand tu les utilises.
- Si tu veux réutiliser les groupes plusieurs fois — pense à faire .ToList() ou .ToArray() pour “fixer” le résultat.
Utilisation concrète
La groupification permet de faire des rapports, des stats et de l’analytics stylés. Voilà quelques exemples réels où ça sert :
- Dans un e-shop : regrouper les commandes par utilisateur pour afficher l’historique d’achats de chacun.
- Sur une plateforme éducative : regrouper les étudiants par prof ou par matière.
- Dans une appli RH : regrouper les employés par département et compter le headcount.
- En entretien : on peut te demander d’écrire une groupification de vraies données avec agrégation.
LINQ permet de prototyper ce genre de trucs super vite, et savoir lire/écrire des groupifications — c’est un gros plus pour tout dev .NET.
Schéma visuel
Voilà un schéma simple du traitement d’une collection avec GroupBy :
[Collection d’étudiants]
|
[GroupBy sur ClassName]
|
[Groupe "9A"] ---> [Ivan, Oleg]
[Groupe "10B"] --> [Maria, Svetlana]
Chaque groupe — c’est une mini-collection avec une clé.
6. Pièges et erreurs fréquentes
Certains étudiants se mélangent un peu au début à cause du résultat spécial de GroupBy :
- Après avoir appelé GroupBy tu n’obtiens pas juste des “groupes” genre List<List<Student>>, mais une collection IGrouping<TKey, TElement>. Pour bosser avec les groupes, utilise la propriété Key et itère sur les éléments du groupe.
- Groupification sur une clé complexe (plusieurs champs) : pense à utiliser un type anonyme ou à créer une classe dédiée si tu veux une clé structurée.
- Si tu veux juste le nombre d’éléments dans les groupes — oublie pas de faire Select(g => g.Count()), sinon tu devras compter à la main dans chaque groupe.
- Parfois tu peux te perdre dans les boucles imbriquées : la première sur les groupes, la deuxième sur les éléments du groupe.
- Si tu te plantes dans la lambda ou la clé, tu n’auras pas les groupes attendus (ou un seul gros groupe si la clé est la même partout).
GO TO FULL VERSION