group clause (C#-verwijzing)

De group component retourneert een reeks IGrouping<TKey,TElement> objecten die nul of meer items bevatten die overeenkomen met de sleutelwaarde voor de groep. U kunt bijvoorbeeld een reeks tekenreeksen groeperen op basis van de eerste letter in elke tekenreeks. In dit geval is de eerste letter de sleutel en heeft een typeteken en wordt opgeslagen in de Key eigenschap van elk IGrouping<TKey,TElement> object. De compiler bepaalt het type sleutel.

U kunt een query-expressie beëindigen met een group component, zoals wordt weergegeven in het volgende voorbeeld:

// Query variable is an IEnumerable<IGrouping<char, Student>>
var studentQuery1 =
    from student in students
    group student by student.Last[0];

Als u extra querybewerkingen voor elke groep wilt uitvoeren, kunt u een tijdelijke id opgeven met behulp van het contextuele trefwoord. Wanneer u de query gebruikt into, moet u doorgaan met de query en deze uiteindelijk beëindigen met een select instructie of een andere group component, zoals wordt weergegeven in het volgende fragment:

// Group students by the first letter of their last name
// Query variable is an IEnumerable<IGrouping<char, Student>>
var studentQuery2 =
    from student in students
    group student by student.Last[0] into g
    orderby g.Key
    select g;

In de sectie Voorbeeld van dit artikel vindt u meer volledige voorbeelden van het gebruik van group met en zonder into .

De resultaten van een groepsquery opsommen

Omdat de IGrouping<TKey,TElement> objecten die door een group query worden geproduceerd, in feite een lijst met lijsten zijn, moet u een geneste foreach-lus gebruiken om toegang te krijgen tot de items in elke groep. De buitenste lus doorloopt de groepssleutels en de binnenste lus doorloopt elk item in de groep zelf. Een groep heeft mogelijk een sleutel, maar geen elementen. Hier volgt de foreach lus waarmee de query wordt uitgevoerd in de vorige codevoorbeelden:

// Iterate group items with a nested foreach. This IGrouping encapsulates
// a sequence of Student objects, and a Key of type char.
// For convenience, var can also be used in the foreach statement.
foreach (IGrouping<char, Student> studentGroup in studentQuery2)
{
     Console.WriteLine(studentGroup.Key);
     // Explicit type for student could also be used here.
     foreach (var student in studentGroup)
     {
         Console.WriteLine("   {0}, {1}", student.Last, student.First);
     }
 }

Sleuteltypen

Groepssleutels kunnen elk type zijn, zoals een tekenreeks, een ingebouwd numeriek type of een door de gebruiker gedefinieerd benoemd type of anoniem type.

Groeperen op tekenreeks

In de vorige codevoorbeelden is een char. In plaats daarvan kan eenvoudig een tekenreekssleutel zijn opgegeven, bijvoorbeeld de volledige achternaam:

// Same as previous example except we use the entire last name as a key.
// Query variable is an IEnumerable<IGrouping<string, Student>>
var studentQuery3 =
    from student in students
    group student by student.Last;

Groeperen op bool

In het volgende voorbeeld ziet u het gebruik van een boolwaarde voor een sleutel om de resultaten in twee groepen te verdelen. Houd er rekening mee dat de waarde wordt geproduceerd door een subexpressie in de group component.

class GroupSample1
{
    // The element type of the data source.
    public class Student
    {
        public required string First { get; init; }
        public required string Last { get; init; }
        public required int ID { get; init; }
        public required List<int> Scores;
    }

    public static List<Student> GetStudents()
    {
        // Use a collection initializer to create the data source. Note that each element
        //  in the list contains an inner sequence of scores.
        List<Student> students =
        [
           new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= [97, 72, 81, 60]},
           new Student {First="Claire", Last="O'Donnell", ID=112, Scores= [75, 84, 91, 39]},
           new Student {First="Sven", Last="Mortensen", ID=113, Scores= [99, 89, 91, 95]},
           new Student {First="Cesar", Last="Garcia", ID=114, Scores= [72, 81, 65, 84]},
           new Student {First="Debra", Last="Garcia", ID=115, Scores= [97, 89, 85, 82]}
        ];

        return students;
    }

    static void Main()
    {
        // Obtain the data source.
        List<Student> students = GetStudents();

        // Group by true or false.
        // Query variable is an IEnumerable<IGrouping<bool, Student>>
        var booleanGroupQuery =
            from student in students
            group student by student.Scores.Average() >= 80; //pass or fail!

        // Execute the query and access items in each group
        foreach (var studentGroup in booleanGroupQuery)
        {
            Console.WriteLine(studentGroup.Key == true ? "High averages" : "Low averages");
            foreach (var student in studentGroup)
            {
                Console.WriteLine("   {0}, {1}:{2}", student.Last, student.First, student.Scores.Average());
            }
        }
    }
}
/* Output:
  Low averages
   Omelchenko, Svetlana:77.5
   O'Donnell, Claire:72.25
   Garcia, Cesar:75.5
  High averages
   Mortensen, Sven:93.5
   Garcia, Debra:88.25
*/

Groeperen op numeriek bereik

In het volgende voorbeeld wordt een expressie gebruikt om numerieke groepssleutels te maken die een percentielbereik vertegenwoordigen. Let op het gebruik van let als een handige locatie om een resultaat van een methode-aanroep op te slaan, zodat u de methode niet twee keer hoeft aan te roepen in de group component. Zie Uitzonderingen in query-expressies afhandelen voor meer informatie over het veilig gebruiken van methoden in query-expressies.

class GroupSample2
{
    // The element type of the data source.
    public class Student
    {
        public required string First { get; init; }
        public required string Last { get; init; }
        public required int ID { get; init; }
        public required List<int> Scores;
    }

    public static List<Student> GetStudents()
    {
        // Use a collection initializer to create the data source. Note that each element
        //  in the list contains an inner sequence of scores.
        List<Student> students =
        [
           new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= [97, 72, 81, 60]},
           new Student {First="Claire", Last="O'Donnell", ID=112, Scores= [75, 84, 91, 39]},
           new Student {First="Sven", Last="Mortensen", ID=113, Scores= [99, 89, 91, 95]},
           new Student {First="Cesar", Last="Garcia", ID=114, Scores= [72, 81, 65, 84]},
           new Student {First="Debra", Last="Garcia", ID=115, Scores= [97, 89, 85, 82]}
        ];

        return students;
    }

    // This method groups students into percentile ranges based on their
    // grade average. The Average method returns a double, so to produce a whole
    // number it is necessary to cast to int before dividing by 10.
    static void Main()
    {
        // Obtain the data source.
        List<Student> students = GetStudents();

        // Write the query.
        var studentQuery =
            from student in students
            let avg = (int)student.Scores.Average()
            group student by (avg / 10) into g
            orderby g.Key
            select g;

        // Execute the query.
        foreach (var studentGroup in studentQuery)
        {
            int temp = studentGroup.Key * 10;
            Console.WriteLine("Students with an average between {0} and {1}", temp, temp + 10);
            foreach (var student in studentGroup)
            {
                Console.WriteLine("   {0}, {1}:{2}", student.Last, student.First, student.Scores.Average());
            }
        }
    }
}
/* Output:
     Students with an average between 70 and 80
       Omelchenko, Svetlana:77.5
       O'Donnell, Claire:72.25
       Garcia, Cesar:75.5
     Students with an average between 80 and 90
       Garcia, Debra:88.25
     Students with an average between 90 and 100
       Mortensen, Sven:93.5
 */

Groeperen op samengestelde sleutels

Gebruik een samengestelde sleutel als u elementen wilt groeperen op basis van meer dan één sleutel. U maakt een samengestelde sleutel met behulp van een anoniem type of een benoemd type voor het opslaan van het sleutelelement. In het volgende voorbeeld wordt ervan uitgegaan dat een klasse Person is gedeclareerd met leden met de naam surname en city. De group component zorgt ervoor dat een afzonderlijke groep wordt gemaakt voor elke set personen met dezelfde achternaam en dezelfde plaats.

group person by new {name = person.surname, city = person.city};

Gebruik een benoemd type als u de queryvariabele moet doorgeven aan een andere methode. Maak een speciale klasse met behulp van automatisch geïmplementeerde eigenschappen voor de sleutels en overschrijf vervolgens de Equals en GetHashCode methoden. U kunt ook een struct gebruiken, in welk geval u deze methoden niet strikt hoeft te overschrijven. Zie Een lichtgewicht klasse implementeren met automatisch geïmplementeerde eigenschappen en query's uitvoeren op dubbele bestanden in een mapstructuur voor meer informatie. Het laatste artikel bevat een codevoorbeeld dat laat zien hoe u een samengestelde sleutel gebruikt met een benoemd type.

Voorbeeld 1

In het volgende voorbeeld ziet u het standaardpatroon voor het ordenen van brongegevens in groepen wanneer er geen extra querylogica wordt toegepast op de groepen. Dit wordt een groepering genoemd zonder vervolg. De elementen in een matrix met tekenreeksen worden gegroepeerd op basis van hun eerste letter. Het resultaat van de query is een IGrouping<TKey,TElement> type dat een openbare Key eigenschap van het type char bevat en een IEnumerable<T> verzameling die elk item in de groepering bevat.

Het resultaat van een group component is een reeks reeksen. Gebruik daarom voor toegang tot de afzonderlijke elementen binnen elke geretourneerde groep een geneste foreach lus binnen de lus die de groepssleutels doorloopt, zoals wordt weergegeven in het volgende voorbeeld.

class GroupExample1
{
    static void Main()
    {
        // Create a data source.
        string[] words = ["blueberry", "chimpanzee", "abacus", "banana", "apple", "cheese"];

        // Create the query.
        var wordGroups =
            from w in words
            group w by w[0];

        // Execute the query.
        foreach (var wordGroup in wordGroups)
        {
            Console.WriteLine("Words that start with the letter '{0}':", wordGroup.Key);
            foreach (var word in wordGroup)
            {
                Console.WriteLine(word);
            }
        }
    }
}
/* Output:
      Words that start with the letter 'b':
        blueberry
        banana
      Words that start with the letter 'c':
        chimpanzee
        cheese
      Words that start with the letter 'a':
        abacus
        apple
     */

Voorbeeld 2

In dit voorbeeld ziet u hoe u extra logica uitvoert voor de groepen nadat u ze hebt gemaakt, met behulp van een vervolg met into. Zie voor meer informatie. In het volgende voorbeeld wordt elke groep opgevraagd om alleen de groep te selecteren waarvan de sleutelwaarde een klinker is.

class GroupClauseExample2
{
    static void Main()
    {
        // Create the data source.
        string[] words2 = ["blueberry", "chimpanzee", "abacus", "banana", "apple", "cheese", "elephant", "umbrella", "anteater"];

        // Create the query.
        var wordGroups2 =
            from w in words2
            group w by w[0] into grps
            where (grps.Key == 'a' || grps.Key == 'e' || grps.Key == 'i'
                   || grps.Key == 'o' || grps.Key == 'u')
            select grps;

        // Execute the query.
        foreach (var wordGroup in wordGroups2)
        {
            Console.WriteLine("Groups that start with a vowel: {0}", wordGroup.Key);
            foreach (var word in wordGroup)
            {
                Console.WriteLine("   {0}", word);
            }
        }
    }
}
/* Output:
    Groups that start with a vowel: a
        abacus
        apple
        anteater
    Groups that start with a vowel: e
        elephant
    Groups that start with a vowel: u
        umbrella
*/

Opmerkingen

Tijdens het compileren group worden componenten omgezet in aanroepen naar de GroupBy methode.

Zie ook