Nota
L'accés a aquesta pàgina requereix autorització. Pots provar d'iniciar sessió o canviar de directori.
L'accés a aquesta pàgina requereix autorització. Pots provar de canviar directoris.
La group cláusula devuelve una secuencia de IGrouping<TKey,TElement> objetos . La secuencia contiene cero o más elementos que coinciden con el valor de clave del grupo. Por ejemplo, puede agrupar una secuencia de cadenas según la primera letra de cada cadena. En este caso, la primera letra es la clave y tiene un carácter de tipo. Cada IGrouping<TKey,TElement> objeto almacena esta clave en su Key propiedad. El compilador deduce el tipo de la clave.
La documentación de referencia del lenguaje C# cubre la versión más reciente publicada del lenguaje C#. También contiene documentación inicial sobre las características de las versiones preliminares públicas de la próxima versión del lenguaje.
La documentación identifica cualquier característica introducida por primera vez en las últimas tres versiones del idioma o en las versiones preliminares públicas actuales.
Sugerencia
Para buscar cuándo se introdujo por primera vez una característica en C#, consulte el artículo sobre el historial de versiones del lenguaje C#.
Puede finalizar una expresión de consulta con una cláusula group, como se muestra en el ejemplo siguiente:
// Query variable is an IEnumerable<IGrouping<char, Student>>
var studentQuery1 =
from student in students
group student by student.Last[0];
Si desea realizar operaciones de consulta adicionales en cada grupo, especifique un identificador temporal mediante la palabra clave into contextual. Cuando se usa into, es necesario continuar con la consulta y finalmente terminarla con una instrucción select u otra cláusula group, como se muestra en el extracto siguiente:
// 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;
En la sección Ejemplo de este artículo se proporcionan ejemplos más completos sobre el uso de group con y sin into.
Enumeración de los resultados de una consulta de grupo
Dado que los IGrouping<TKey,TElement> objetos que genera una group consulta son básicamente una lista de listas, debe usar un bucle foreach anidado para tener acceso a los elementos de cada grupo. El bucle exterior recorre en iteración las claves de grupo y el bucle interno recorre en iteración cada elemento del propio grupo. Un grupo puede tener una clave pero ningún elemento. El siguiente foreach bucle ejecuta la consulta en los ejemplos de código anteriores:
// 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);
}
}
Tipos de clave
Las claves de grupo pueden ser de cualquier tipo, como una cadena, un tipo numérico integrado, un tipo con nombre definido por el usuario o un tipo anónimo.
Agrupar por cadena
En los ejemplos de código anteriores se ha usado el tipo char. También puede especificar una clave de cadena, como el apellido completo:
// 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;
Agrupar por valor booleano
En el ejemplo siguiente se usa un valor bool para una clave para dividir los resultados en dos grupos. El valor procede de una subexpresión en la group cláusula .
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
*/
Agrupar por intervalo numérico
En el ejemplo siguiente se usa una expresión para crear claves de grupo numéricas que representan un intervalo de percentil. Usa let para almacenar un resultado de llamada de método, por lo que no es necesario llamar al método dos veces en la group cláusula . Para más información sobre cómo usar métodos en expresiones de consulta de manera segura, consulte Controlar excepciones en expresiones de consulta.
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
*/
Agrupación por claves compuestas
Use una clave compuesta cuando quiera agrupar elementos por más de una clave. Cree una clave compuesta mediante un tipo anónimo o un tipo con nombre para contener el elemento de clave. En el ejemplo siguiente, supongamos que una clase Person tiene miembros denominados surname y city. La group cláusula crea un grupo independiente para cada conjunto de personas con el mismo apellido y la misma ciudad.
group person by new {name = person.surname, city = person.city};
Use un tipo con nombre si necesita pasar la variable de consulta a otro método. Cree una clase especial con propiedades implementadas automáticamente para las claves y, a continuación, invalide los Equals métodos y GetHashCode . También puede usar una estructura, que no requiere estrictamente esas invalidaciones de método. Para obtener más información, vea Cómo implementar una clase ligera con propiedades implementadas automáticamente y Cómo consultar archivos duplicados en un árbol de directorios. El último artículo contiene un ejemplo de código en el que se muestra cómo usar una clave compuesta con un tipo con nombre.
Examples
En el ejemplo siguiente se muestra el patrón estándar para ordenar los datos de origen en grupos cuando no se aplica ninguna lógica de consulta adicional a los grupos. Este patrón se denomina agrupación sin una continuación. En el ejemplo se agrupan los elementos de una matriz de cadenas según su primera letra. El resultado de la consulta es un tipo IGrouping<TKey,TElement> que contiene una propiedad Key pública de tipo char y una colección IEnumerable<T> que contiene cada elemento de la agrupación.
El resultado de una cláusula group es una secuencia de secuencias. Para acceder a los elementos individuales dentro de cada grupo devuelto, use un bucle anidado foreach dentro del bucle que recorre en iteración las claves de grupo, como se muestra en el ejemplo siguiente.
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
*/
En el ejemplo siguiente se muestra cómo realizar lógica adicional en los grupos después de crearlos mediante una continuación con into. Para obtener más información, consulte into. En el ejemplo siguiente se consulta cada grupo para seleccionar solo aquellos cuyo valor de clave sea una vocal.
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
*/
En tiempo de compilación, el compilador traduce group cláusulas en llamadas al GroupBy método .
La sintaxis de la consulta de group cláusulas no admite el comparador de igualdad personalizado. Si quiere usar IEqualityComparer en la consulta, use explícitamente el GroupBy método .