group, clause (Référence C#)
La clause group retourne une séquence d'objets IGrouping qui contiennent zéro ou plus d'éléments qui correspondent à la valeur de clé pour le groupe. Par exemple, vous pouvez grouper une séquence de chaînes d'après la première lettre de chaque chaîne. Dans ce cas, la première lettre est la clé, a un type char et est stockée dans la propriété Key de chaque objet IGrouping. Le compilateur déduit le type de la clé.
Vous pouvez terminer une expression de requête avec une clause group, comme indiqué 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, vous pouvez spécifier un identificateur temporaire en utilisant le mot clé contextuel into. Lorsque vous utilisez into, vous devez continuer la requête et finalement la terminer avec une instruction select ou une autre clause group, comme indiqué 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 plus complets d'utilisation de group avec et sans into sont fournis dans la section Exemples de cette rubrique.
Énumération des résultats d'une requête de groupe
Étant donné que les objets IGrouping produits par une requête group 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 au sein des clés de groupe et la boucle interne itère au sein de chaque élément du groupe lui-même. Un groupe peut avoir une clé, mais aucun élément. Voici à quoi ressemble la boucle foreach qui 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 groupe peuvent être de tout type, comme une chaîne, un type numérique intégré; un type nommé défini par l'utilisateur ou un type anonyme.
Regroupement par chaîne
Les exemples de code précédents ont utilisé un char. Une clé de chaîne aurait pu être spécifiée facilement à la place, par exemple le nom de famille 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 booléen
L'exemple suivant illustre l'utilisation d'une valeur booléenne pour une clé pour diviser les résultats en deux groupes. Notez que la valeur est produite par une sous-expression dans la clause 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
*/
Regroupement par plage numérique.
L'exemple suivant utilise une expression pour créer des clés de groupe numériques qui représentent une plage de centiles. Notez l'utilisation de let en tant qu'emplacement adéquat pour stocker un résultat d'appel de méthode, de sorte que vous n'avez pas à appeler la méthode deux fois dans la clause group. Notez également dans la clause group que pour éviter une exception « division par zéro », le code vérifie que l'étudiant n'a pas une moyenne de zéro. Pour plus d'informations sur l'utilisation sans risque de méthodes dans les expressions de requête, consultez Comment : gérer des exceptions dans des expressions de requête (Guide de programmation C#).
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 == 0 ? 0 : 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
*/
Regroupement par clés composites
Utilisez une clé composite lorsque vous souhaitez grouper des éléments d'après plusieurs clés. Vous créez une clé composite en utilisant un type anonyme ou un type nommé pour conserver l'élément clé. Dans l'exemple suivant, supposons qu'une classe Person a été déclarée avec les membres nommés surname et city. La clause group provoque la création d'un groupe séparé pour chaque ensemble de personnes avec 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 à l'aide de propriétés implémentées automatiquement pour les clés, puis substituez les méthodes Equals et GetHashCode. Vous pouvez également utiliser un struct, auquel cas il n'est pas strictement nécessaire de substituer ces méthodes. Pour plus d'informations, consultez Comment : implémenter une classe Lightweight avec des propriétés implémentées automatiquement (Guide de programmation C#) et Comment : interroger des fichiers dupliqués dans une arborescence de répertoires (LINQ). La dernière rubrique présente un exemple de code qui illustre comment utiliser une clé composite avec un type nommé.
Exemple
L'exemple suivant affiche le modèle standard pour organiser les données source en groupes lorsque aucune logique de requête supplémentaire n'est appliquée aux groupes. On appelle ceci un regroupement sans continuation. Les éléments d'un tableau de chaînes sont groupés d'après leur première lettre. Le résultat de la requête est un type IGrouping qui contient une propriété Key publique de type char et une collection IEnumerable qui contient chaque élément dans le regroupement.
Le résultat d'une clause group est une séquence de séquences. Par conséquent, pour accéder aux éléments individuels dans chaque groupe retourné, utilisez une boucle foreach imbriquée à l'intérieur de la boucle qui itère au sein des clés de groupe, comme indiqué 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 '{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
*/
Cet exemple indique comment exécuter une logique supplémentaire sur les groupes après les avoir créés, en utilisant une continuation avec into. Pour plus d'informations, consultez into (Référence C#). 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: {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
*/
Notes
À la compilation, les clauses group sont traduites en appels à la méthode GroupBy``2.
Voir aussi
Tâches
Comment : créer un groupe imbriqué (Guide de programmation C#)
Comment : regrouper les résultats d'une requête (Guide de programmation C#)
Comment : effectuer une sous-requête sur une opération de regroupement (Guide de programmation C#)
Référence
Concepts
Expressions de requête LINQ (Guide de programmation C#)