CodeGym /Kurse /C# SELF /Daten gruppieren mit group b...

Daten gruppieren mit group by

C# SELF
Level 32 , Lektion 0
Verfügbar

1. Einführung

Gruppierung von Daten bedeutet, Elemente einer Collection in "Körbe" zu sortieren, je nach gemeinsamem Merkmal. Stell dir vor, du sortierst Äpfel nach Farbe: Alle roten kommen in einen Korb, die grünen in einen anderen, die gelben irgendwo anders hin. In der Programmierung nennt man das Gruppierung.

Wozu braucht man das?
In echten Aufgaben ist es oft praktisch, Leute nach Stadt, Produkte nach Kategorie, Transaktionen nach Datum usw. zu gruppieren. Das ermöglicht nützliche Analysen: Zum Beispiel herausfinden, wie viele Studenten es in jeder Klasse gibt oder welche Städte am meisten Studenten haben.

LINQ bietet dafür eine spezielle Methode: GroupBy. Es gibt auch einen passenden Operator im Query-Syntax – das ist group by. Lass uns das Ganze mit Beispielen in unserer Lern-App anschauen – einem System zur Verwaltung von Studenten und Klassen.

2. GroupBy im Method-Syntax

Grundlegende Signatur und Rückgabewert

Die Methode GroupBy sieht erstmal etwas wild aus:


IEnumerable<IGrouping<TKey, TElement>> GroupBy<TElement, TKey>(
    this IEnumerable<TElement> source,
    Func<TElement, TKey> keySelector
)

Wo:

  • source — die Ausgangs-Collection (zum Beispiel eine Liste von Studenten).
  • keySelector — Funktion, nach der das Gruppierungsmerkmal bestimmt wird, zum Beispiel das Property ClassName oder City.

Wichtig!
Das Ergebnis ist nicht einfach ein Array deiner Objekte, sondern eine Collection von speziellen Gruppen (IGrouping<TKey, TElement>). Jede Gruppe enthält:

  • Den Gruppenschlüssel (Key – das, was die Elemente verbindet),
  • Die Collection der Elemente, die in diese Gruppe gefallen sind.

Schaubild:

Schlüssel (Key) Gruppenelemente (Objektgruppe)
"9A" Ivan, Oleg, Maria
"10B" Svetlana, Petr
... ...

Beispiel 1: Studenten nach Klassenname gruppieren

Nehmen wir an, wir haben eine Klasse Student:


public class Student
{
    public string Name { get; set; }
    public string ClassName { get; set; }
    public int Grade { get; set; }
}

Und eine Collection von Studenten:


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

Wir gruppieren die Studenten nach Klasse:


var studentsByClass = students.GroupBy(s => s.ClassName);

foreach (var group in studentsByClass)
{
    Console.WriteLine($"Klasse: {group.Key}"); // Gruppenschlüssel
    foreach (var student in group)
    {
        Console.WriteLine($" - {student.Name}, Note: {student.Grade}");
    }
}

Was passiert hier?
Die Methode GroupBy hat für uns zwei Gruppen erstellt: eine für "9A", eine für "10B". Wenn wir die Gruppen durchgehen, können wir beliebige aggregierte Infos zu jedem "Haufen" ausgeben.

Visualisierung: Wie sieht die Collection nach GroupBy aus?

Man kann sich das so vorstellen:


students.GroupBy(s => s.ClassName)
        ├─ Gruppe "9A" { Ivan, Oleg }
        └─ Gruppe "10B" { Maria, Svetlana }

Jeder "Ast" ist eine eigene Mini-Liste von Studenten, die nach Schlüssel zusammengehören.

Studenten nach Note: Gruppierung nach beliebigen Merkmalen

Lass uns mal nicht nach Klasse, sondern nach Note gruppieren!


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} aus Klasse {student.ClassName}");
    }
}

Ausgabe:


Note: 5
 - Ivan aus Klasse 9A
 - Maria aus Klasse 10B
Note: 4
 - Oleg aus Klasse 9A
Note: 3
 - Svetlana aus Klasse 10B

3. Gruppen in LINQ weiterverarbeiten: Was kann man nach GroupBy machen?

Sehr oft will man nach der Gruppierung nicht einfach nur die Gruppen ausgeben, sondern irgendeine aggregierte Info bekommen – zum Beispiel die Anzahl der Elemente pro Gruppe zählen, den Durchschnitt berechnen oder eine Namensliste sammeln.

Beispiel 2: Anzahl der Studenten pro Klasse zählen


var classCounts = students
    .GroupBy(s => s.ClassName)
    .Select(g => new { Class = g.Key, Count = g.Count() });

foreach (var cc in classCounts)
{
    Console.WriteLine($"In Klasse {cc.Class} — {cc.Count} Studenten");
}

Ergebnis:


In Klasse 9A — 2 Studenten
In Klasse 10B — 2 Studenten
  • Erst haben wir die Studenten in Gruppen aufgeteilt.
  • Dann für jede Gruppe ein anonymes Objekt mit Schlüssel (Klassenname) und Anzahl (Methode Count()) gebaut.

Beispiel 3: Liste der Einser-Schüler pro Klasse sammeln


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($"In Klasse {group.Class} Einser-Schüler: {string.Join(", ", group.ExcellentStudents)}");
}

4. group by im Query-Syntax

Wenn du auf etwas stehst, das wie SQL aussieht, oder aus der Datenbankwelt kommst, dann unterstützt LINQ auch Query-Syntax wie group by ... into ....

Basisbeispiel für Gruppierung


var studentsByClass =
    from s in students
    group s by s.ClassName into classGroup
    select classGroup;

foreach (var group in studentsByClass)
{
    Console.WriteLine($"Klasse: {group.Key}");
    foreach (var student in group)
    {
        Console.WriteLine($" - {student.Name}");
    }
}

Erst "gruppieren" wir nach dem Merkmal, dann geben wir der Gruppe einen Namen (into classGroup) – und können dann damit machen, was wir wollen.

Projektion mit Aggregaten

Angenommen, wir wollen die Städte und die Anzahl der Studenten pro Stadt wissen:


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($"In Klasse {item.Class} — {item.Count} Studenten");
}

— Der Syntax ist derselbe, aber nach select kannst du jede Projektion machen (Aggregation, Listen, Durchschnittswerte usw.).

5. Weitere Szenarien

Gruppierung nach mehreren Merkmalen (zusammengesetzter Schlüssel)

Manchmal muss man nach einer Kombination von Properties gruppieren. Zum Beispiel nach Klasse und Note:


var byClassAndGrade = students
    .GroupBy(s => new { s.ClassName, s.Grade });

foreach (var group in byClassAndGrade)
{
    Console.WriteLine($"Klasse: {group.Key.ClassName}, Note: {group.Key.Grade}");
    foreach (var student in group)
    {
        Console.WriteLine($" - {student.Name}");
    }
}

Der Gruppenschlüssel ist jetzt ein anonymer Typ (Kombination mehrerer Felder).

Wie funktionieren Gruppen in der Praxis?

Ein bisschen LINQ-Interna

  • Wenn du GroupBy machst, baut LINQ intern eine spezielle "Tabelle": Für jeden einzigartigen Schlüsselwert gibt es eine eigene Gruppe.
  • Gruppen implementieren das Interface IGrouping<TKey, TElement>. Du kannst auf das Property Key zugreifen, um den Wert des Gruppierungsmerkmals zu bekommen.

Lazy Evaluation und Performance

  • Gruppen werden nicht sofort berechnet, sondern erst, wenn du sie durchläufst (foreach). Also, auch bei sehr großen Collections – keine Angst vor "Gruppen-Erstellung", solange du sie nicht benutzt, existieren sie nur virtuell.
  • Wenn du öfter auf die Gruppen zugreifen willst – ruf .ToList() oder .ToArray() auf, um das Ergebnis "einzufrieren".

Praxiseinsatz

Gruppierung ermöglicht es, komplexe Reports, Statistiken und Analysen zu bauen. Hier ein paar echte Beispiele, wo das nützlich ist:

  • Im Online-Shop: Bestellungen nach User gruppieren, um die Einkaufshistorie jedes Users zu zeigen.
  • Auf einer Lernplattform: Studenten nach Lehrer oder nach Fach gruppieren.
  • Im HR-Tool: Mitarbeiter nach Abteilung gruppieren und Headcount zählen.
  • Im Vorstellungsgespräch: Es kann sein, dass du eine Gruppierung echter Daten mit Aggregation schreiben sollst.

Mit LINQ kann man solche Aufgaben super schnell prototypen, und Gruppierungen lesen und schreiben zu können ist ein fetter Pluspunkt für jeden .NET-Entwickler.

Visuelles Schema

Hier ein einfaches Blockdiagramm, wie eine Collection mit GroupBy verarbeitet wird:


[Studenten-Collection] 
      |
[GroupBy nach ClassName]
      |
[Gruppe "9A"] ---> [Ivan, Oleg]
[Gruppe "10B"] --> [Maria, Svetlana]

Jede Gruppe ist eine eigene Mini-Collection mit Schlüssel.

6. Häufige Fehler und Stolperfallen

Manche Studenten sind am Anfang verwirrt wegen der Besonderheiten des GroupBy-Ergebnisses:

  • Nach dem Aufruf von GroupBy bekommst du nicht einfach "Gruppen" vom Typ List<List<Student>>, sondern eine Collection von IGrouping<TKey, TElement>. Um mit Gruppen zu arbeiten, nutze das Property Key und iteriere über die Elemente der Gruppe.
  • Gruppierung nach komplexem Schlüssel (mehrere Felder): Vergiss nicht, einen anonymen Typ zu verwenden oder eine eigene Klasse zu bauen, wenn du einen strukturierten Schlüssel brauchst.
  • Wenn du nur die Anzahl der Elemente in Gruppen wissen willst – nutze Select(g => g.Count()), sonst musst du die Elemente in jeder Gruppe manuell zählen.
  • Man kann sich leicht in verschachtelten Schleifen verirren: Die erste Schleife geht über die Gruppen, die zweite über die Elemente in der Gruppe.
  • Wenn du dich bei der Lambda oder dem Schlüssel vertust, bekommst du nicht die Gruppen, die du erwartest (oder nur eine große Gruppe, wenn alle denselben Schlüssel haben).
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION