group (Cláusula, Referencia de C#)group clause (C# Reference)

La cláusula group devuelve una secuencia de objetos IGrouping<TKey,TElement> que contienen cero o más elementos que coinciden con el valor de clave del grupo.The group clause returns a sequence of IGrouping<TKey,TElement> objects that contain zero or more items that match the key value for the group. Por ejemplo, puede agrupar una secuencia de cadenas según la primera letra de cada cadena.For example, you can group a sequence of strings according to the first letter in each string. En este caso, la primera letra es la clave, es de tipo char y se almacena en la propiedad Key de cada objeto IGrouping<TKey,TElement>.In this case, the first letter is the key and has a type char, and is stored in the Key property of each IGrouping<TKey,TElement> object. El compilador deduce el tipo de la clave.The compiler infers the type of the key.

Puede finalizar una expresión de consulta con una cláusula group, como se muestra en el ejemplo siguiente:You can end a query expression with a group clause, as shown in the following example:

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

Si quiere realizar operaciones de consulta adicionales en cada grupo, puede especificar un identificador temporal mediante la palabra clave contextual into.If you want to perform additional query operations on each group, you can specify a temporary identifier by using the into contextual keyword. 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:When you use into, you must continue with the query, and eventually end it with either a select statement or another group clause, as shown in the following excerpt:

// 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.More complete examples of the use of group with and without into are provided in the Example section of this article.

Enumeración de los resultados de una consulta de grupoEnumerating the results of a group query

Dado que los objetos IGrouping<TKey,TElement> generados por una consulta group son esencialmente una lista de listas, debe usar un bucle foreach anidado para tener acceso a los elementos de cada grupo.Because the IGrouping<TKey,TElement> objects produced by a group query are essentially a list of lists, you must use a nested foreach loop to access the items in each group. El bucle exterior recorre en iteración las claves de grupo y el bucle interno recorre en iteración cada elemento del propio grupo.The outer loop iterates over the group keys, and the inner loop iterates over each item in the group itself. Un grupo puede tener una clave pero ningún elemento.A group may have a key but no elements. A continuación se muestra el bucle foreach que ejecuta la consulta en los ejemplos de código anteriores:The following is the foreach loop that executes the query in the previous code examples:

// 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 claveKey types

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.Group keys can be any type, such as a string, a built-in numeric type, or a user-defined named type or anonymous type.

Agrupar por cadenaGrouping by string

En los ejemplos de código anteriores se ha usado el tipo char.The previous code examples used a char. En su lugar, podría haberse especificado una clave de cadena, por ejemplo, los apellidos completos:A string key could easily have been specified instead, for example the complete last name:

// 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 booleanoGrouping by bool

En el ejemplo siguiente se muestra el uso de un valor booleano para una clave con el fin de dividir los resultados en dos grupos.The following example shows the use of a bool value for a key to divide the results into two groups. Observe que el valor se genera a partir de una subexpresión en la cláusula group.Note that the value is produced by a sub-expression in the group clause.

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

Agrupar por intervalo numéricoGrouping by numeric range

En el ejemplo siguiente se usa una expresión para crear claves de grupo numéricas que representan un intervalo de percentil.The next example uses an expression to create numeric group keys that represent a percentile range. Observe el uso de let como ubicación adecuada para almacenar el resultado de una llamada de método, para no tener que llamar al método dos veces en la cláusula group.Note the use of let as a convenient location to store a method call result, so that you don't have to call the method two times in the group clause. Para obtener más información sobre cómo usar métodos en expresiones de consulta de manera segura, vea Cómo: Controlar excepciones en expresiones de consulta.For more information about how to safely use methods in query expressions, see How to: Handle Exceptions in Query Expressions.

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

Agrupación por claves compuestasGrouping by composite keys

Use una clave compuesta cuando quiera agrupar los elementos según más de una clave.Use a composite key when you want to group elements according to more than one key. Para crear una clave compuesta se usa un tipo anónimo o un tipo con nombre para contener el elemento de clave.You create a composite key by using an anonymous type or a named type to hold the key element. En el ejemplo siguiente, supongamos que una clase Person se ha declarado con los miembros denominados surname y city.In the following example, assume that a class Person has been declared with members named surname and city. La cláusula group hace que se cree un grupo independiente para cada conjunto de personas con el mismo apellido y la misma ciudad.The group clause causes a separate group to be created for each set of persons with the same last name and the same city.

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

Use un tipo con nombre si debe pasar la variable de consulta a otro método.Use a named type if you must pass the query variable to another method. Cree una clase especial usando las propiedades autoimplementadas para las claves y, luego, invalide los métodos Equals y GetHashCode.Create a special class using auto-implemented properties for the keys, and then override the Equals and GetHashCode methods. También puede usar un struct, en cuyo caso no es estrictamente necesario invalidar esos métodos.You can also use a struct, in which case you do not strictly have to override those methods. Para obtener más información, vea Cómo: Implementar una clase ligera con propiedades autoimplementadas y Cómo: Buscar archivos duplicados en un árbol de directorios.For more information see How to: Implement a Lightweight Class with Auto-Implemented Properties and How to: Query for Duplicate Files in a Directory Tree. 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.The latter article has a code example that demonstrates how to use a composite key with a named type.

EjemploExample

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.The following example shows the standard pattern for ordering source data into groups when no additional query logic is applied to the groups. Esto se denomina agrupación sin continuación.This is called a grouping without a continuation. Los elementos de una matriz de cadenas se agrupan por la primera letra.The elements in an array of strings are grouped according to their first letter. 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.The result of the query is an IGrouping<TKey,TElement> type that contains a public Key property of type char and an IEnumerable<T> collection that contains each item in the grouping.

El resultado de una cláusula group es una secuencia de secuencias.The result of a group clause is a sequence of sequences. Por consiguiente, para tener acceso a los elementos individuales de cada grupo devuelto, use un bucle foreach anidado dentro del bucle que recorre en iteración las claves de grupo, como se muestra en el ejemplo siguiente.Therefore, to access the individual elements within each returned group, use a nested foreach loop inside the loop that iterates the group keys, as shown in the following example.

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

EjemploExample

En este ejemplo se muestra cómo aplicar lógica adicional a los grupos después de haberlos creado, mediante el uso de una continuación con into.This example shows how to perform additional logic on the groups after you have created them, by using a continuation with into. Para obtener más información, vea into.For more information, see into. En el ejemplo siguiente se consulta cada grupo para seleccionar solo aquellos cuyo valor de clave sea una vocal.The following example queries each group to select only those whose key value is a vowel.

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

ComentariosRemarks

En tiempo de compilación, las cláusulas group se convierten en llamadas al método GroupBy.At compile time, group clauses are translated into calls to the GroupBy method.

Vea tambiénSee also