Partage via


group, clause (Référence C#)

La group clause retourne une séquence d’objets IGrouping<TKey,TElement> . La séquence contient zéro ou plusieurs éléments qui correspondent à la valeur de clé du groupe. Par exemple, vous pouvez regrouper une séquence de chaînes en fonction de la première lettre de chaque chaîne. Dans ce cas, la première lettre est la clé et a un caractère de type. Chaque IGrouping<TKey,TElement> objet stocke cette clé dans sa Key propriété. Le compilateur déduit le type de la clé.

La documentation de référence du langage C# décrit la version la plus récente du langage C#. Il contient également la documentation initiale des fonctionnalités dans les préversions publiques pour la prochaine version du langage.

La documentation identifie toute fonctionnalité introduite en premier dans les trois dernières versions de la langue ou dans les préversions publiques actuelles.

Conseil / Astuce

Pour savoir quand une fonctionnalité a été introduite en C#, consultez l’article sur l’historique des versions du langage C#.

Vous pouvez terminer une expression de requête avec une clause group, comme illustré dans l’exemple suivant :

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

Si vous souhaitez effectuer des opérations de requête supplémentaires sur chaque groupe, spécifiez un identificateur temporaire à l’aide du mot clé contextuel. Quand vous utilisez into, vous devez continuer la requête et finalement la terminer avec une instruction select ou une autre clause group, comme illustré dans l’extrait suivant :

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

Des exemples d’utilisation plus complets de group avec et sans into sont fournis dans la section Exemple de cet article.

Énumération des résultats d’une requête de groupe

Étant donné que les IGrouping<TKey,TElement> objets qu’une group requête produit sont essentiellement une liste de listes, vous devez utiliser une boucle foreach imbriquée pour accéder aux éléments de chaque groupe. La boucle externe itère les clés de groupe, et la boucle interne itère chaque élément dans le groupe proprement dit. Un groupe peut avoir une clé, mais aucun élément. La boucle suivante foreach exécute la requête dans les exemples de code précédents :

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

Types de clés

Les clés de groupes peuvent être de tout type, comme une chaîne, un type numérique intégré, ou un type nommé ou un type anonyme défini par l’utilisateur.

Regroupement par chaîne

Les exemples de code précédents utilisaient un char. Vous pouvez également spécifier une clé de chaîne, telle que le nom complet :

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

Regroupement par valeur booléenne

L’exemple suivant utilise une valeur bool pour une clé pour diviser les résultats en deux groupes. La valeur provient d’une sous-expression dans la group clause.

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

Regroupement par plage numérique

L’exemple suivant utilise une expression pour créer des clés de groupes numériques qui représentent une plage de centiles. Il utilise let pour stocker un résultat d’appel de méthode. Vous n’avez donc pas besoin d’appeler la méthode deux fois dans la group clause. Pour plus d’informations sur la façon d’utiliser en toute sécurité des méthodes dans des expressions de requête, consultez Guide pratique pour gérer des exceptions dans des expressions de requête.

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

Regroupement par clés composites

Utilisez une clé composite lorsque vous souhaitez regrouper des éléments par plusieurs touches. Créez une clé composite à l’aide d’un type anonyme ou d’un type nommé pour contenir l’élément clé. Dans l’exemple suivant, supposons qu’une classe Person a des membres nommés surname et city. La group clause crée un groupe distinct pour chaque ensemble de personnes portant le même nom et la même ville.

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

Utilisez un type nommé si vous devez passer la variable de requête à une autre méthode. Créez une classe spéciale avec des propriétés implémentées automatiquement pour les clés, puis remplacez les méthodes et GetHashCode les Equals méthodes. Vous pouvez également utiliser un struct, qui ne nécessite pas strictement ces remplacements de méthode. Pour plus d’informations, consultez Comment implémenter une classe légère avec des propriétés implémentées automatiquement et comment interroger des fichiers en double dans une arborescence de répertoires. Ce dernier article contient un exemple de code qui montre comment utiliser une clé composite avec un type nommé.

Examples

L’exemple suivant montre le modèle standard pour classer les données sources dans des groupes lorsque vous n’appliquez aucune logique de requête supplémentaire aux groupes. Ce modèle est appelé regroupement sans continuation. L’exemple regroupe les éléments d’un tableau de chaînes en fonction de leur première lettre. Le résultat de la requête est un type IGrouping<TKey,TElement> contenant une propriété Key publique de type char et une collection IEnumerable<T> qui contient chaque élément du regroupement.

Le résultat d’une clause group est une séquence de séquences. Pour accéder aux éléments individuels au sein de chaque groupe retourné, utilisez une boucle imbriquée foreach à l’intérieur de la boucle qui itère les clés de groupe, comme illustré dans l’exemple suivant.

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

L’exemple suivant montre comment effectuer une logique supplémentaire sur les groupes après les avoir créés, à l’aide d’une continuation avec into. Pour plus d’informations, consultez into. L’exemple suivant interroge chaque groupe pour sélectionner uniquement ceux dont la valeur de clé est une voyelle.

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

Au moment de la compilation, le compilateur convertit des clauses group en appels à la GroupBy méthode.

La syntaxe de la requête de clause ne prend pas en charge le comparateur d’égalité group personnalisé. Si vous souhaitez utiliser IEqualityComparer dans votre requête, utilisez explicitement la GroupBy méthode.

Voir aussi