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

Klauzule group vrátí sekvenci IGrouping<TKey,TElement> objektů, které obsahují 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ísmene v každém řetězci. V tomto případě je první písmeno klíčem a má znak typu a je uloženo ve Key vlastnosti každého IGrouping<TKey,TElement> objektu. Kompilátor odvodí typ klíče.

Výraz dotazu můžete ukončit klauzulí group , 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 u každé skupiny provádět další operace dotazů, můžete zadat dočasný identifikátor pomocí klíčového slova into contextual. Při použití příkazu intomusíte pokračovat v dotazu a nakonec ho ukončit příkazem select nebo jinou group klauzulí, 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> Vzhledem k tomu, že objekty vytvořené dotazem group jsou v podstatě seznamem seznamů, musíte pro přístup k položkám v každé skupině použít vnořenou smyčku foreach. Vnější smyčka iteruje nad klíči skupiny a vnitřní smyčka iteruje nad každou položkou v samotné skupině. Skupina může mít klíč, ale žádné prvky. Následuje smyčka foreach , která spustí 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íčů

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

Seskupení podle řetězce

Předchozí příklady kódu používaly char. Místo toho by se dal snadno zadat řetězcový klíč, například celé 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 bool

Následující příklad ukazuje použití logické hodnoty klíče k rozdělení výsledků do dvou skupin. Všimněte si, že hodnota je vytvořená dílčím výrazem v klauzuli group .

class GroupSample1
{
    // The element type of the data source.
    public class Student
    {
        public string First { get; set; }
        public string Last { get; set; }
        public int ID { get; set; }
        public 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 List<Student>
        {
           new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 72, 81, 60}},
           new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
           new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {99, 89, 91, 95}},
           new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {72, 81, 65, 84}},
           new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {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());
            }
        }

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* 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

V dalším příkladu se pomocí výrazu vytvoří klíče číselné skupiny, které představují rozsah percentilu. Všimněte si použití funkce let jako vhodného umístění pro uložení výsledku volání metody, abyste nemuseli v group klauzuli volat metodu dvakrát. Další informace o tom, jak bezpečně používat metody ve výrazech dotazů, najdete v tématu Zpracování výjimek ve výrazech dotazů.

class GroupSample2
{
    // The element type of the data source.
    public class Student
    {
        public string First { get; set; }
        public string Last { get; set; }
        public int ID { get; set; }
        public 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 List<Student>
        {
           new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 72, 81, 60}},
           new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
           new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {99, 89, 91, 95}},
           new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {72, 81, 65, 84}},
           new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {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());
            }
        }

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* 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
 */

Seskupení podle složených klíčů

Složený klíč použijte, pokud chcete seskupit prvky podle více než jednoho klíče. Složený klíč vytvoříte pomocí anonymního typu nebo pojmenovaného typu, který bude obsahovat prvek klíče. V následujícím příkladu předpokládejme, že byla deklarována třída Person se členy s názvem surname a city. Klauzule group způsobí vytvoření samostatné skupiny pro každou skupinu 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 pomocí automaticky implementovaných vlastností klíčů a potom přepište Equals metody a GetHashCode . Můžete také použít strukturu, v takovém případě není nutné tyto metody přepsat. Další informace najdete v tématech Jak implementovat odlehčenou třídu s automaticky implementovanými vlastnostmi a Dotazování na duplicitní soubory v adresářovém stromu. Druhý článek obsahuje příklad kódu, který ukazuje, jak použít složený klíč s pojmenovaným typem.

Příklad 1

Následující příklad ukazuje standardní vzor pro řazení zdrojových dat do skupin, pokud se na skupiny nepoužívá žádná další logika dotazu. Říká se tomu seskupení bez pokračování. Prvky v poli řetězců jsou seskupeny podle jejich prvního písmene. 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 klauzule group je posloupnost sekvencí. Proto pro přístup k jednotlivým prvkům v rámci každé vrácené skupiny použijte uvnitř smyčky vnořenou foreach smyčku, 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 '{0}':", wordGroup.Key);
            foreach (var word in wordGroup)
            {
                Console.WriteLine(word);
            }
        }

        // Keep the console window open in debug mode
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* 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
     */

Příklad 2

Tento příklad ukazuje, jak u skupin po jejich vytvoření provést další logiku pomocí pokračování s into. Další informace najdete v tématu. Následující příklad dotazuje každou skupinu a vybere pouze ty, jejichž hodnotou 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: {0}", wordGroup.Key);
            foreach (var word in wordGroup)
            {
                Console.WriteLine("   {0}", word);
            }
        }

        // Keep the console window open in debug mode
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* 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
*/

Poznámky

V době kompilace se group klauzule překládají na GroupBy volání metody .

Viz také