CodeGym /Cours /C# SELF /Regrouper des données avec g...

Regrouper des données avec group by

C# SELF
Niveau 32 , Leçon 0
Disponible

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).
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION