gruppsats (C#-referens)

- group satsen returnerar en sekvens med IGrouping<TKey,TElement> objekt som innehåller noll eller fler objekt som matchar nyckelvärdet för gruppen. Du kan till exempel gruppera en sekvens med strängar enligt den första bokstaven i varje sträng. I det här fallet är den första bokstaven nyckeln och har en typtecken och lagras i Key egenskapen för varje IGrouping<TKey,TElement> objekt. Kompilatorn härleder typen av nyckel.

Du kan avsluta ett frågeuttryck med en group sats, som du ser i följande exempel:

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

Om du vill utföra ytterligare frågeåtgärder för varje grupp kan du ange en tillfällig identifierare med hjälp av det kontextuella nyckelordet. När du använder intomåste du fortsätta med frågan och slutligen avsluta den med antingen en instruktion eller en select annan group sats, som du ser i följande utdrag:

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

Mer fullständiga exempel på användning av group med och utan into finns i avsnittet Exempel i den här artikeln.

Räkna upp resultatet av en gruppfråga

Eftersom objekten IGrouping<TKey,TElement> som skapas av en group fråga i princip är en lista med listor måste du använda en kapslad foreach-loop för att komma åt objekten i varje grupp. Den yttre loopen itererar över gruppnycklarna och den inre loopen itererar över varje objekt i själva gruppen. En grupp kan ha en nyckel men inga element. Följande är den foreach loop som kör frågan i föregående kodexempel:

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

Nyckeltyper

Gruppnycklar kan vara valfri typ, till exempel en sträng, en inbyggd numerisk typ eller en användardefinierad namngiven typ eller anonym typ.

Gruppera efter sträng

I föregående kodexempel användes en char. En strängnyckel kunde enkelt ha angetts i stället, till exempel det fullständiga efternamn:

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

Gruppering efter bool

I följande exempel visas användningen av ett bool-värde för en nyckel för att dela upp resultatet i två grupper. Observera att värdet skapas av ett underuttryck i group -satsen.

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
*/

Gruppera efter numeriskt intervall

I nästa exempel används ett uttryck för att skapa numeriska gruppnycklar som representerar ett percentilintervall. Observera användningen av let som en lämplig plats för att lagra ett metodanropsresultat, så att du inte behöver anropa metoden två gånger i group -satsen. Mer information om hur du använder metoder i frågeuttryck på ett säkert sätt finns i Hantera undantag i frågeuttryck.

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
 */

Gruppera efter sammansatta nycklar

Använd en sammansatt nyckel när du vill gruppera element enligt mer än en nyckel. Du skapar en sammansatt nyckel med hjälp av en anonym typ eller en namngiven typ för att lagra nyckelelementet. I följande exempel antar du att en klass Person har deklarerats med medlemmar med namnet surname och city. Satsen group gör att en separat grupp skapas för varje uppsättning personer med samma efternamn och samma stad.

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

Använd en namngiven typ om du måste skicka frågevariabeln till en annan metod. Skapa en särskild klass med hjälp av automatiskt implementerade egenskaper för nycklarna och åsidosätt Equals sedan metoderna och GetHashCode . Du kan också använda en struct, i vilket fall du inte strikt behöver åsidosätta dessa metoder. Mer information finns i Så här implementerar du en lättviktsklass med automatiskt implementerade egenskaper och Så här frågar du efter dubblettfiler i ett katalogträd. Den senare artikeln har ett kodexempel som visar hur du använder en sammansatt nyckel med en namngiven typ.

Exempel 1

I följande exempel visas standardmönstret för att sortera källdata i grupper när ingen ytterligare frågelogik tillämpas på grupperna. Detta kallas för en gruppering utan fortsättning. Elementen i en matris med strängar grupperas enligt deras första bokstav. Resultatet av frågan är en IGrouping<TKey,TElement> typ som innehåller en offentlig Key egenskap av typen char och en IEnumerable<T> samling som innehåller varje objekt i gruppering.

Resultatet av en group sats är en sekvens med sekvenser. För att komma åt de enskilda elementen i varje returnerad grupp använder du därför en kapslad foreach loop i loopen som itererar gruppnycklarna, som du ser i följande exempel.

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
     */

Exempel 2

Det här exemplet visar hur du utför ytterligare logik i grupperna när du har skapat dem med hjälp av en fortsättning med into. Mer information finns i. I följande exempel uppmanas varje grupp att endast välja de vars nyckelvärde är en vokal.

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
*/

Kommentarer

Vid kompileringstillfället group översätts satser till anrop till GroupBy metoden.

Se även