Share via


Gewusst wie: Gruppieren von Abfrageergebnissen (C#-Programmierhandbuch)

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, used in GroupByRange. 
    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 Fall ist der Schlüssel ein string, der Nachname des Studenten. Es ist auch möglich, eine Teilzeichenfolge für den Schlüssel zu verwenden. Der Gruppierungsvorgang verwendet den Standardgleichheitsvergleich für den Typ.

Fügen Sie die folgende Methode in die StudentClass-Klasse ein. Ändern Sie die Aufrufanweisung in der Main-Methode in sc.GroupBySingleProperty().

public void GroupBySingleProperty()
{
    Console.WriteLine("Group by a single property in an object:");

    // Variable queryLastNames is an IEnumerable<IGrouping<string,  
    // DataClass.Student>>.  
    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: Adams
            Adams, Terry
    Key: Fakhouri
            Fakhouri, Fadi
    Key: Feng
            Feng, Hanying
    Key: Garcia
            Garcia, Cesar
            Garcia, Debra
            Garcia, Hugo
    Key: Mortensen
            Mortensen, Sven
    Key: O'Donnell
            O'Donnell, Claire
    Key: Omelchenko
            Omelchenko, Svetlana
    Key: Tucker
            Tucker, Lance
            Tucker, Michael
    Key: Zabokritski
            Zabokritski, Eugene
*/

Im folgenden Beispiel wird veranschaulicht, wie Sie Quellelemente gruppieren können, indem Sie einen anderen Gruppenschlüssel als die Objekteigenschaft verwenden. In diesem Beispiel ist der Schlüssel der erste Buchstabe des Nachnamens des Studenten.

Fügen Sie die folgende Methode in die StudentClass-Klasse ein. Ändern Sie die Aufrufanweisung in der Main-Methode in sc.GroupBySubstring().

public 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 something other than a property of the object:
    Key: A
            Adams, Terry
    Key: F
            Fakhouri, Fadi
            Feng, Hanying
    Key: G
            Garcia, Cesar
            Garcia, Debra
            Garcia, Hugo
    Key: M
            Mortensen, Sven
    Key: O
            O'Donnell, Claire
            Omelchenko, Svetlana
    Key: T
            Tucker, Lance
            Tucker, Michael
    Key: Z
            Zabokritski, Eugene
*/

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. Die Methode gibt einen ganzzahligen Wert zwischen 0 und 10 zurück.

//Helper method, used in GroupByRange. 
protected static int GetPercentile(Student s)
{
    double avg = s.ExamScores.Average();
    return avg > 0 ? (int)avg / 10 : 0;
}

Fügen Sie die folgende Methode in die StudentClass-Klasse ein. Ändern Sie die Aufrufanweisung in der Main-Methode in sc.GroupByRange().

public 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
            O'Donnell, Claire
    Key: 80
            Adams, Terry
            Feng, Hanying
            Garcia, Cesar
            Garcia, Hugo
            Mortensen, Sven
            Omelchenko, Svetlana
            Tucker, Lance
            Zabokritski, Eugene
    Key: 90
            Fakhouri, Fadi
            Tucker, Michael
*/

Im folgenden Beispiel wird veranschaulicht, wie Sie Quellelemente gruppieren können, indem Sie einen booleschen Vergleichsausdruck verwenden. In diesem Beispiel überprüft der boolesche Ausdruck, ob die Prüfungsdurchschnittsbewertung eines Studenten größer als 75 ist. 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.

Fügen Sie die folgende Methode in die StudentClass-Klasse ein. Ändern Sie die Aufrufanweisung in der Main-Methode in sc.GroupByBoolean().

public 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
            Terry Adams
            Fadi Fakhouri
            Hanying Feng
            Cesar Garcia
            Hugo Garcia
            Sven Mortensen
            Svetlana Omelchenko
            Lance Tucker
            Michael Tucker
            Eugene Zabokritski
    Key: False
            Debra Garcia
            Claire O'Donnell
*/

Im folgenden Beispiel wird veranschaulicht, wie Sie einen anonymen Typ zum Kapseln eines Schlüssels mit mehreren Werten verwenden können. In diesem Beispiel ist der erste Schlüsselwert der erste Buchstabe des Nachnamens des Studenten. Der zweite Schlüsselwert ist ein boolescher Wert, der angibt, ob der Student in der ersten Prüfung einen Wert über 85 erzielt hat. Sie können die Gruppen anhand jeder Eigenschaft im Schlüssel sortieren.

Fügen Sie die folgende Methode in die StudentClass-Klasse ein. Ändern Sie die Aufrufanweisung in der Main-Methode in sc.GroupByCompositeKey().

public 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 A who scored more than 85
            Terry Adams
    Name starts with F who scored more than 85
            Fadi Fakhouri
            Hanying Feng
    Name starts with G who scored more than 85
            Cesar Garcia
            Hugo 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 less than 85
            Claire O'Donnell
    Name starts with O who scored more than 85
            Svetlana Omelchenko
    Name starts with T who scored less than 85
            Lance Tucker
    Name starts with T who scored more than 85
            Michael Tucker
    Name starts with Z who scored more than 85
            Eugene Zabokritski
*/

Kompilieren des Codes

Kopieren Sie jede Methode, die Sie testen möchten, in die StudentClass-Klasse. Fügen Sie der Main-Methode eine Aufrufanweisung für die Methode hinzu, und drücken Sie F5.

Wenn Sie diese Methoden an die eigene Anwendung anpassen, beachten Sie, dass LINQ .NET Framework Version 3.5 oder 4 erfordert 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 using-Direktiven 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: Erstellen einer geschachtelten Gruppe (C#-Programmierhandbuch)

Referenz

group-Klausel (C#-Referenz)

Anonyme Typen (C#-Programmierhandbuch)

GroupBy``2

IGrouping

Konzepte

LINQ-Abfrageausdrücke (C#-Programmierhandbuch)

Gruppieren von Daten