Gewusst wie: Gruppieren von Ergebnissen auf verschiedene Weisen (C#-Programmierhandbuch)
Aktualisiert: November 2007
Das Gruppieren ist eine der leistungsstärksten Funktionen von LINQ. In den folgenden Beispielen wird gezeigt, wie Daten auf verschiedene Weisen gruppiert werden:
Anhand einer einzelnen Eigenschaft.
Anhand des ersten Buchstabens einer Zeichenfolgeneigenschaft.
Anhand eines berechneten numerischen Bereichs.
Anhand eines booleschen Prädikats oder anderen Ausdrucks.
Anhand eines zusammengesetzten Schlüssels.
Darüber hinaus projizieren die letzten beiden Abfragen die Ergebnisse in einen neuen anonymen Typ, der nur den Vor- und Nachnamen des Studenten enthält. Weitere Informationen finden Sie unter group-Klausel (C#-Referenz).
Beispiel
Alle Beispiele in diesem Thema nutzen die folgenden Hilfsklassen und Datenquellen.
public class StudentClass
{
#region data
protected enum GradeLevel { FirstYear = 1, SecondYear, ThirdYear, FourthYear };
protected class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int ID { get; set; }
public GradeLevel Year;
public List<int> ExamScores;
}
protected static List<Student> students = new List<Student>
{
new Student {FirstName = "Terry", LastName = "Adams", ID = 120, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 99, 82, 81, 79}},
new Student {FirstName = "Fadi", LastName = "Fakhouri", ID = 116, Year = GradeLevel.ThirdYear,ExamScores = new List<int>{ 99, 86, 90, 94}},
new Student {FirstName = "Hanying", LastName = "Feng", ID = 117, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 93, 92, 80, 87}},
new Student {FirstName = "Cesar", LastName = "Garcia", ID = 114, Year = GradeLevel.FourthYear,ExamScores = new List<int>{ 97, 89, 85, 82}},
new Student {FirstName = "Debra", LastName = "Garcia", ID = 115, Year = GradeLevel.ThirdYear, ExamScores = new List<int>{ 35, 72, 91, 70}},
new Student {FirstName = "Hugo", LastName = "Garcia", ID = 118, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 92, 90, 83, 78}},
new Student {FirstName = "Sven", LastName = "Mortensen", ID = 113, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 88, 94, 65, 91}},
new Student {FirstName = "Claire", LastName = "O'Donnell", ID = 112, Year = GradeLevel.FourthYear, ExamScores = new List<int>{ 75, 84, 91, 39}},
new Student {FirstName = "Svetlana", LastName = "Omelchenko", ID = 111, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 97, 92, 81, 60}},
new Student {FirstName = "Lance", LastName = "Tucker", ID = 119, Year = GradeLevel.ThirdYear, ExamScores = new List<int>{ 68, 79, 88, 92}},
new Student {FirstName = "Michael", LastName = "Tucker", ID = 122, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 94, 92, 91, 91}},
new Student {FirstName = "Eugene", LastName = "Zabokritski", ID = 121, Year = GradeLevel.FourthYear, ExamScores = new List<int>{ 96, 85, 91, 60}}
};
#endregion
//Helper method
protected static int GetPercentile(Student s)
{
double avg = s.ExamScores.Average();
return avg > 0 ? (int)avg / 10 : 0;
}
public void QueryHighScores(int exam, int score)
{
var highScores = from student in students
where student.ExamScores[exam] > score
select new {Name = student.FirstName, Score = student.ExamScores[exam]};
foreach (var item in highScores)
{
Console.WriteLine("{0,-15}{1}", item.Name, item.Score);
}
}
}
public class Program
{
public static void Main()
{
StudentClass sc = new StudentClass();
sc.QueryHighScores(1, 90);
// Keep the console window open in debug mode
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
Im folgenden Beispiel wird veranschaulicht, wie Sie Quellelemente gruppieren können, indem Sie eine einzelne Eigenschaft des Elements als Gruppenschlüssel verwenden. In diesem Beispiel ist der Schlüssel eine string. Es ist auch möglich, eine Teilzeichenfolge für den Schlüssel zu verwenden. Der Gruppierungsvorgang verwendet den Standardgleichheitsvergleich für den Typ.
private static void GroupBySingleProperty()
{
Console.WriteLine("Group by a single property in an object");
// queryLastNames is an IEnumerable<IGrouping<string, DataClass.Student>>
// var is easier to type.
var queryLastNames =
from student in students
group student by student.LastName into newGroup
orderby newGroup.Key
select newGroup;
foreach (var nameGroup in queryLastNames)
{
Console.WriteLine("Key: {0}", nameGroup.Key);
foreach (var student in nameGroup)
{
Console.WriteLine("\t{0}, {1}", student.LastName, student.FirstName);
}
}
}
/* Output:
Group by a single property in an object
Key: Feng
Feng, Hanying
Key: Garcia
Garcia, Hugo
Garcia, Cesar
Garcia, Debra
Key: Mortensen
Mortensen, Sven
Key: O'Donnell
O'Donnell, Claire
Key: Omelchenko
Omelchenko, Svetlana
Key: Tucker
Tucker, Michael
Tucker, Lance
*/
Im folgenden Beispiel wird veranschaulicht, wie Sie Quellelemente gruppieren können, indem Sie einen anderen Gruppenschlüssel als die Objekteigenschaft verwenden.
private static void GroupBySubstring()
{
Console.WriteLine("\r\nGroup by something other than a property of the object:");
var queryFirstLetters =
from student in students
group student by student.LastName[0];
foreach (var studentGroup in queryFirstLetters)
{
Console.WriteLine("Key: {0}", studentGroup.Key);
// Nested foreach is required to access group items
foreach (var student in studentGroup)
{
Console.WriteLine("\t{0}, {1}", student.LastName, student.FirstName);
}
}
}
/* Output:
Group by first character:
Key: O
Omelchenko, Svetlana
O'Donnell, Claire
Key: G
Garcia, Hugo
Garcia, Cesar
Garcia, Debra
Key: M
Mortensen, Sven
Key: T
Tucker, Michael
Tucker, Lance
Key: F
Feng, Hanying
*/
Im folgenden Beispiel wird veranschaulicht, wie Sie Quellelemente gruppieren können, indem Sie einen numerischen Bereich als Gruppenschlüssel verwenden. Die Abfrage projiziert dann die Ergebnisse in einen anonymen Typ, der nur den Vor- und Nachnamen sowie den prozentualen Bereich des Studenten enthält. Der anonyme Typ wird verwendet, da nicht das gesamte Student-Objekt zur Anzeige der Ergebnisse verwendet werden muss. GetPercentile ist eine Hilfsfunktion, mit der ein prozentualer Bereich anhand der Durchschnittsbewertung des Studenten berechnet wird:
static int GetPercentile(Student s)
{
double avg = s.Scores.Average();
return avg > 0 ? (int)avg / 10 : 0;
}
private static void GroupByRange()
{
Console.WriteLine("\r\nGroup by numeric range and project into a new anonymous type:");
var queryNumericRange =
from student in students
let percentile = GetPercentile(student)
group new { student.FirstName, student.LastName } by percentile into percentGroup
orderby percentGroup.Key
select percentGroup;
// Nested foreach required to iterate over groups and group items.
foreach (var studentGroup in queryNumericRange)
{
Console.WriteLine("Key: {0}", (studentGroup.Key * 10));
foreach (var item in studentGroup)
{
Console.WriteLine("\t{0}, {1}", item.LastName, item.FirstName);
}
}
}
/* Output:
Group by numeric range and project into a new anonymous type:
Key: 60
Garcia, Debra
Key: 70
Omelchenko, Svetlana
O'Donnell, Claire
Key: 80
Garcia, Hugo
Mortensen, Sven
Garcia, Cesar
Feng, Hanying
Tucker, Lance
Key: 90
Tucker, Michael
*/
Im folgenden Beispiel wird veranschaulicht, wie Sie Quellelemente gruppieren können, indem Sie einen booleschen Vergleichsausdruck verwenden. Wie in vorherigen Beispielen werden die Ergebnisse in einen anonymen Typ projiziert, da nicht das gesamte Quellelement benötigt wird. Beachten Sie, dass die Eigenschaften im anonymen Typ zu Eigenschaften auf dem Key-Member werden und beim Ausführen der Abfrage der Zugriff auf sie anhand des Namens möglich ist.
private static void GroupByBoolean()
{
Console.WriteLine("\r\nGroup by a boolean into two groups with string keys");
Console.WriteLine("\"True\" and \"False\" and project into a new anonymous type:");
var queryGroupByAverages = from student in students
group new { student.FirstName, student.LastName }
by student.ExamScores.Average() > 75 into studentGroup
select studentGroup;
foreach (var studentGroup in queryGroupByAverages)
{
Console.WriteLine("Key: {0}", studentGroup.Key);
foreach (var student in studentGroup)
Console.WriteLine("\t{0} {1}", student.FirstName, student.LastName);
}
}
/* Output:
Group by a boolean into two groups with string keys
"True" and "False" and project into a new anonymous type:
Key: True
Svetlana Omelchenko
Hugo Garcia
Sven Mortensen
Michael Tucker
Cesar Garcia
Hanying Feng
Lance Tucker
Key: False
Claire O'Donnell
Debra Garcia
*/
Im folgenden Beispiel wird veranschaulicht, wie Sie einen anonymen Typ zum Kapseln eines Schlüssels mit mehreren Werten verwenden können. In diesem Fall ist der zweite Schlüsselwert ein boolescher Wert, der angibt, ob der Student in der ersten Prüfung einen Wert über 85 hatte. Sie können die Gruppen anhand jeder Eigenschaft im Schlüssel sortieren.
private static void GroupByCompositeKey()
{
var queryHighScoreGroups =
from student in students
group student by new { FirstLetter = student.LastName[0], Score = student.ExamScores[0] > 85 } into studentGroup
orderby studentGroup.Key.FirstLetter
select studentGroup;
Console.WriteLine("\r\nGroup and order by a compound key:");
foreach (var scoreGroup in queryHighScoreGroups)
{
string s = scoreGroup.Key.Score == true ? "more than" : "less than";
Console.WriteLine("Name starts with {0} who scored {1} 85", scoreGroup.Key.FirstLetter, s);
foreach (var item in scoreGroup)
{
Console.WriteLine("\t{0} {1}", item.FirstName, item.LastName);
}
}
}
/* Output:
Group and order by a compound key:
Name starts with F who scored more than 85
Hanying Feng
Name starts with G who scored more than 85
Hugo Garcia
Cesar Garcia
Name starts with G who scored less than 85
Debra Garcia
Name starts with M who scored more than 85
Sven Mortensen
Name starts with O who scored more than 85
Svetlana Omelchenko
Name starts with O who scored less than 85
Claire O'Donnell
Name starts with T who scored more than 85
Michael Tucker
Name starts with T who scored less than 85
Lance Tucker
*/
Kompilieren des Codes
Dieses Beispiel enthält Verweise auf Objekte, die in der Beispielanwendung in Gewusst wie: Abfragen einer Auflistung von Objekten (C#-Programmierhandbuch) definiert werden. Um diese Methode zu kompilieren und auszuführen, fügen Sie sie in die StudentClass-Klasse dieser Anwendung ein, und fügen Sie ihr von der Main-Methode einen Aufruf hinzu.
Wenn Sie diese Methode an Ihre eigene Anwendung anpassen, beachten Sie, dass LINQ .NET Framework Version 3.5 benötigt und dass das Projekt einen Verweis auf System.Core.dll enthalten und eine Direktive für System.Linq verwenden muss. LINQ-to-SQL-, LINQ-to-XML- und LINQ-to-DataSet-Typen erfordern zusätzliche Usings und Verweise. Weitere Informationen hierzu finden Sie unter Gewusst wie: Erstellen eines LINQ-Projekts.
Siehe auch
Aufgaben
Gewusst wie: Ausführen einer Unterabfrage für eine Gruppierungsoperation (C#-Programmierhandbuch)
Gewusst wie: Gruppieren einer Gruppe (C#-Programmierhandbuch)
Konzepte
LINQ-Abfrageausdrücke (C#-Programmierhandbuch)