Sdílet prostřednictvím


klauzule group (Referenční dokumentace jazyka C#)

Klauzule group vrátí sekvenci IGrouping<TKey,TElement> objektů. Sekvence obsahuje nula nebo více položek, které odpovídají hodnotě klíče skupiny. Můžete například seskupit posloupnost řetězců podle prvního písmena v každém řetězci. V tomto případě je první písmeno klíčem a má znak typu. Každý IGrouping<TKey,TElement> objekt ukládá tento klíč do své Key vlastnosti. Kompilátor odvodí typ klíče.

Referenční dokumentace jazyka C# dokumentuje naposledy vydané verze jazyka C#. Obsahuje také počáteční dokumentaci k funkcím ve verzi Public Preview pro nadcházející jazykovou verzi.

Dokumentace identifikuje všechny funkce, které byly poprvé představeny v posledních třech verzích jazyka nebo v aktuálních verzích Public Preview.

Návod

Informace o tom, kdy byla funkce poprvé představena v jazyce C#, najdete v článku o historii verzí jazyka C#.

Výraz group dotazu můžete ukončit klauzulí, jak je znázorněno v následujícím příkladu:

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

Pokud chcete pro každou skupinu provádět další operace dotazu, zadejte dočasný identifikátor pomocí kontextového klíčového slova. Při použití into musíte pokračovat s dotazem a nakonec ho ukončit příkazem select nebo jinou klauzulí group, jak je znázorněno v následujícím výňatku:

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

Podrobnější příklady použití group s a bez into jsou uvedeny v části Příkladů tohoto článku.

Vytvoření výčtu výsledků skupinového dotazu

IGrouping<TKey,TElement> Protože objekty, které group dotaz vytváří, jsou v podstatě seznam seznamů, je nutné použít vnořenou smyčku foreach pro přístup k položkám v každé skupině. Vnější smyčka iteruje přes klíče skupiny a vnitřní smyčka iteruje přes každou položku v samotné skupině. Skupina může mít klíč, ale žádné prvky. Následující foreach smyčka provede dotaz v předchozích příkladech kódu:

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

Typy klíčů

Klíče skupiny můžou být libovolný typ, například řetězec, předdefinovaný číselný typ nebo uživatelem definovaný pojmenovaný typ nebo anonymní typ.

Seskupování podle řetězce

Předchozí příklady kódu používaly znak .char Můžete také zadat klíč řetězce, například úplné příjmení:

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

Seskupování podle logické hodnoty

Následující příklad používá logickou hodnotu klíče k rozdělení výsledků do dvou skupin. Hodnota pochází z dílčího výrazu group v klauzuli.

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

Seskupení podle číselného rozsahu

Ve dalším příkladu využíváme výraz k vytvoření numerických klíčů skupin, které představují percentilový rozsah. Používá let se k uložení výsledku volání metody, takže v klauzuli nemusíte metodu group volat dvakrát. Další informace o tom, jak bezpečně používat metody ve výrazech dotazu, najdete v tématu Zpracování výjimek ve výrazech dotazu.

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 {temp} and {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
 */

Seskupování podle složených klíčů

Složený klíč použijte, pokud chcete seskupit prvky podle více než jednoho klíče. Vytvořte složený klíč pomocí anonymního typu nebo pojmenovaného typu pro uložení klíčového prvku. V následujícím příkladu předpokládejme, že třída Person má členy s názvem surname a city. Klauzule group vytvoří samostatnou skupinu pro každou sadu osob se stejným příjmením a stejným městem.

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

Pokud potřebujete předat proměnnou dotazu jiné metodě, použijte pojmenovaný typ. Vytvořte speciální třídu s automaticky implementovanými vlastnostmi pro klíče a potom přepište Equals metody a GetHashCode metody. Můžete také použít strukturu, která nevyžaduje výhradně přepsání těchto metod. Další informace naleznete v tématu Jak implementovat odlehčenou třídu s automaticky implementovanými vlastnostmi a jak dotazovat duplicitní soubory ve stromu adresáře. Druhý článek obsahuje příklad kódu, který ukazuje použití složeného klíče s pojmenovaným typem.

Examples

Následující příklad ukazuje standardní vzor pro řazení zdrojových dat do skupin, pokud u skupin nepoužijete žádnou logiku dalšího dotazu. Tento vzor se nazývá seskupení bez pokračování. Příklad seskupí prvky v poli řetězců podle jejich prvního písmena. Výsledkem dotazu je IGrouping<TKey,TElement> typ, který obsahuje veřejnou Key vlastnost typu char a IEnumerable<T> kolekci, která obsahuje každou položku v seskupení.

Výsledkem group klauzule je posloupnost sekvencí. Pokud chcete získat přístup k jednotlivým prvkům v rámci každé vrácené skupiny, použijte vnořenou foreach smyčku uvnitř smyčky, která iteruje klíče skupiny, jak je znázorněno v následujícím příkladu.

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

Následující příklad ukazuje, jak provést extra logiku pro skupiny po jejich vytvoření pomocí pokračování s into. Další informace najdete v tématu into. Následující příklad dotazuje každou skupinu, aby vybrali pouze ty, jejichž hodnota klíče je samohláska.

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: {wordGroup.Key}");
            foreach (var word in wordGroup)
            {
                Console.WriteLine($"   {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
*/

V době kompilace kompilátor překládá group klauzule do volání GroupBy metody.

Syntaxe group dotazu klauzule nepodporuje vlastní porovnání rovnosti. Pokud chcete v dotazu použít IEqualityComparer , použijte metodu GroupBy explicitně.

Viz také