Partager via


Comment : regrouper les résultats de différentes façons (Guide de programmation C#)

Mise à jour : novembre 2007

Le regroupement est l'une des fonctions les plus puissantes de LINQ. Les exemples suivants indiquent comment grouper des données de plusieurs façons :

  • Par une propriété unique.

  • Par la première lettre d'une propriété de type chaîne.

  • Par une plage numérique calculée.

  • Par prédicat booléen ou une autre expression.

  • Par une clé composée.

De plus, le deux dernières requêtes projettent leurs résultats dans un nouveau type anonyme qui contient uniquement le prénom et le nom de l'étudiant. Pour plus d'informations, consultez group, clause (Référence C#).

Exemple

Tous les exemples de cette rubrique utilisent les classes d'assistance et sources de données suivantes.

public class StudentClass
{
    #region data
    protected enum GradeLevel { FirstYear = 1, SecondYear, ThirdYear, FourthYear };
    protected class Student
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int ID { get; set; }
        public GradeLevel Year;
        public List<int> ExamScores;
    }

    protected static List<Student> students = new List<Student>
    {
        new Student {FirstName = "Terry", LastName = "Adams", ID = 120, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 99, 82, 81, 79}},
        new Student {FirstName = "Fadi", LastName = "Fakhouri", ID = 116, Year = GradeLevel.ThirdYear,ExamScores = new List<int>{ 99, 86, 90, 94}},
        new Student {FirstName = "Hanying", LastName = "Feng", ID = 117, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 93, 92, 80, 87}},
        new Student {FirstName = "Cesar", LastName = "Garcia", ID = 114, Year = GradeLevel.FourthYear,ExamScores = new List<int>{ 97, 89, 85, 82}},
        new Student {FirstName = "Debra", LastName = "Garcia", ID = 115, Year = GradeLevel.ThirdYear, ExamScores = new List<int>{ 35, 72, 91, 70}},
        new Student {FirstName = "Hugo", LastName = "Garcia", ID = 118, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 92, 90, 83, 78}},
        new Student {FirstName = "Sven", LastName = "Mortensen", ID = 113, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 88, 94, 65, 91}},
        new Student {FirstName = "Claire", LastName = "O'Donnell", ID = 112, Year = GradeLevel.FourthYear, ExamScores = new List<int>{ 75, 84, 91, 39}},
        new Student {FirstName = "Svetlana", LastName = "Omelchenko", ID = 111, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 97, 92, 81, 60}},
        new Student {FirstName = "Lance", LastName = "Tucker", ID = 119, Year = GradeLevel.ThirdYear, ExamScores = new List<int>{ 68, 79, 88, 92}},
        new Student {FirstName = "Michael", LastName = "Tucker", ID = 122, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 94, 92, 91, 91}},
        new Student {FirstName = "Eugene", LastName = "Zabokritski", ID = 121, Year = GradeLevel.FourthYear, ExamScores = new List<int>{ 96, 85, 91, 60}}
    };
    #endregion

    //Helper method
    protected static int GetPercentile(Student s)
    {
        double avg = s.ExamScores.Average();
        return avg > 0 ? (int)avg / 10 : 0;
    }



    public void QueryHighScores(int exam, int score)
    {
        var highScores = from student in students
                         where student.ExamScores[exam] > score
                         select new {Name = student.FirstName, Score = student.ExamScores[exam]};

        foreach (var item in highScores)
        {
            Console.WriteLine("{0,-15}{1}", item.Name, item.Score);
        }
    }
}

public class Program
{
    public static void Main()
    {
        StudentClass sc = new StudentClass();
        sc.QueryHighScores(1, 90);

        // Keep the console window open in debug mode
        Console.WriteLine("Press any key to exit");
        Console.ReadKey();
    }
}

L'exemple suivant indique comment grouper des éléments source en utilisant une propriété unique de l'élément comme clé de groupe. Dans ce cas, la clé est de type string. Il est également possible d'utiliser une sous-chaîne pour la clé. L'opération de regroupement utilise le comparateur d'égalité par défaut pour le type.

private static void GroupBySingleProperty()
{
    Console.WriteLine("Group by a single property in an object");

    // queryLastNames is an IEnumerable<IGrouping<string, DataClass.Student>>
    // var is easier to type.
    var queryLastNames =
        from student in students
        group student by student.LastName into newGroup
        orderby newGroup.Key
        select newGroup;

    foreach (var nameGroup in queryLastNames)
    {
        Console.WriteLine("Key: {0}", nameGroup.Key);
        foreach (var student in nameGroup)
        {
            Console.WriteLine("\t{0}, {1}", student.LastName, student.FirstName);
        }
    }
}
/* Output:
  Group by a single property in an object
  Key: Feng
          Feng, Hanying
  Key: Garcia
          Garcia, Hugo
          Garcia, Cesar
          Garcia, Debra
  Key: Mortensen
          Mortensen, Sven
  Key: O'Donnell
          O'Donnell, Claire
  Key: Omelchenko
          Omelchenko, Svetlana
  Key: Tucker
          Tucker, Michael
          Tucker, Lance
 */  

L'exemple suivant indique comment grouper des éléments source en utilisant une méthode différente de la propriété de l'objet pour la clé de groupe.

private static void GroupBySubstring()
{            
    Console.WriteLine("\r\nGroup by something other than a property of the object:");

    var queryFirstLetters =
        from student in students
        group student by student.LastName[0];

    foreach (var studentGroup in queryFirstLetters)
    {
        Console.WriteLine("Key: {0}", studentGroup.Key);
        // Nested foreach is required to access group items
        foreach (var student in studentGroup)
        {
            Console.WriteLine("\t{0}, {1}", student.LastName, student.FirstName);
        }
    }           
}
/* Output:
        Group by first character:
        Key: O
                Omelchenko, Svetlana
                O'Donnell, Claire
        Key: G
                Garcia, Hugo
                Garcia, Cesar
                Garcia, Debra
        Key: M
                Mortensen, Sven
        Key: T
                Tucker, Michael
                Tucker, Lance
        Key: F
                Feng, Hanying
     */

L'exemple suivant indique comment grouper des éléments source en utilisant une plage numérique comme clé de groupe. La requête projette ensuite les résultats dans un type anonyme qui contient uniquement le prénom et le nom, ainsi que la plage de centiles à laquelle appartient l'étudiant. Un type anonyme est utilisé, car il n'est pas nécessaire d'utiliser l'objet Student complet pour afficher les résultats. GetPercentile est une fonction d'assistance qui calcule un centile basé sur le score moyen de l'étudiant :

static int GetPercentile(Student s)
{
   double avg = s.Scores.Average();
   return avg > 0 ? (int)avg / 10 : 0;
}
private static void GroupByRange()
{            
    Console.WriteLine("\r\nGroup by numeric range and project into a new anonymous type:");

    var queryNumericRange =
        from student in students
        let percentile = GetPercentile(student)
        group new { student.FirstName, student.LastName } by percentile into percentGroup
        orderby percentGroup.Key
        select percentGroup;

    // Nested foreach required to iterate over groups and group items.
    foreach (var studentGroup in queryNumericRange)
    {
        Console.WriteLine("Key: {0}", (studentGroup.Key * 10));
        foreach (var item in studentGroup)
        {
            Console.WriteLine("\t{0}, {1}", item.LastName, item.FirstName);
        }
    }            
}
/* Output:
     Group by numeric range and project into a new anonymous type:
     Key: 60
             Garcia, Debra
     Key: 70
             Omelchenko, Svetlana
             O'Donnell, Claire
     Key: 80
             Garcia, Hugo
             Mortensen, Sven
             Garcia, Cesar
             Feng, Hanying
             Tucker, Lance
     Key: 90
             Tucker, Michael
     */

L'exemple suivant indique comment grouper des éléments source à l'aide d'une expression de comparaison booléenne. Comme dans les exemples précédents, les résultats sont projetés dans un type anonyme, car l'élément source complet n'est pas nécessaire. Notez que les propriétés du type anonyme deviennent des propriétés sur le membre Key et sont accessibles par nom lors de l'exécution de la requête.

private static void GroupByBoolean()
{            
    Console.WriteLine("\r\nGroup by a boolean into two groups with string keys");
    Console.WriteLine("\"True\" and \"False\" and project into a new anonymous type:");
    var queryGroupByAverages = from student in students
                               group new { student.FirstName, student.LastName }
                                    by student.ExamScores.Average() > 75 into studentGroup
                               select studentGroup;

    foreach (var studentGroup in queryGroupByAverages)
    {
        Console.WriteLine("Key: {0}", studentGroup.Key);
        foreach (var student in studentGroup)
            Console.WriteLine("\t{0} {1}", student.FirstName, student.LastName);
    }            
}
/* Output:
         Group by a boolean into two groups with string keys
         "True" and "False" and project into a new anonymous type:
         Key: True
                 Svetlana Omelchenko
                 Hugo Garcia
                 Sven Mortensen
                 Michael Tucker
                 Cesar Garcia
                 Hanying Feng
                 Lance Tucker
         Key: False
                 Claire O'Donnell
                 Debra Garcia
*/

L'exemple suivant indique comment utiliser un type anonyme pour encapsuler une clé qui contient plusieurs valeurs. Dans ce cas, la deuxième valeur de clé est une valeur booléenne qui spécifie si l'étudiant a obtenu un score supérieur à 85 lors du premier examen. Vous pouvez classer les groupes en fonction d'une propriété quelconque de la clé.

private static void GroupByCompositeKey()
{

    var queryHighScoreGroups =
        from student in students
        group student by new { FirstLetter = student.LastName[0], Score = student.ExamScores[0] > 85 } into studentGroup
        orderby studentGroup.Key.FirstLetter
        select studentGroup;

    Console.WriteLine("\r\nGroup and order by a compound key:");
    foreach (var scoreGroup in queryHighScoreGroups)
    {
        string s = scoreGroup.Key.Score == true ? "more than" : "less than";
        Console.WriteLine("Name starts with {0} who scored {1} 85", scoreGroup.Key.FirstLetter, s);
        foreach (var item in scoreGroup)
        {
            Console.WriteLine("\t{0} {1}", item.FirstName, item.LastName);
        }
    }
}
/* Output:
          Group and order by a compound key:
          Name starts with F who scored more than 85
                  Hanying Feng
          Name starts with G who scored more than 85
                  Hugo Garcia
                  Cesar Garcia
          Name starts with G who scored less than 85
                  Debra Garcia
          Name starts with M who scored more than 85
                  Sven Mortensen
          Name starts with O who scored more than 85
                  Svetlana Omelchenko
          Name starts with O who scored less than 85
                  Claire O'Donnell
          Name starts with T who scored more than 85
                  Michael Tucker
          Name starts with T who scored less than 85
                  Lance Tucker
       */

Compilation du code

Cet exemple contient des références aux objets définis dans l'exemple d'application dans Comment : interroger une collection d'objets (Guide de programmation C#). Pour compiler et exécuter cette méthode, collez-la dans la classe StudentClass de cette application et ajoutez-lui un appel de la méthode Main.

When you adapt this method to your own application, remember that LINQ requires version 3.5 of the .NET Framework, and the project must contain a reference to System.Core.dll and a using directive for System.Linq. LINQ to SQL, LINQ to XML and LINQ to DataSet types require additional usings and references. Pour plus d'informations, consultez Comment : créer un projet LINQ.

Voir aussi

Tâches

Comment : effectuer une sous-requête sur une opération de regroupement (Guide de programmation C#)

Comment : regrouper un groupe (Guide de programmation C#)

Concepts

Expressions de requête LINQ (Guide de programmation C#)

Regroupement de données

Référence

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

Types anonymes (Guide de programmation C#)

GroupBy

IGrouping<TKey, TElement>