Share via


cláusula de grupo (Referência C#)

A group cláusula retorna uma sequência de objetos que contêm zero ou mais itens que correspondem ao valor da IGrouping<TKey,TElement> chave para o grupo. Por exemplo, você pode agrupar uma sequência de cadeias de caracteres de acordo com a primeira letra em cada cadeia de caracteres. Neste caso, a primeira letra é a chave e tem um tipo char, e é armazenada na Key propriedade de cada IGrouping<TKey,TElement> objeto. O compilador infere o tipo da chave.

Você pode encerrar uma expressão de consulta com uma group cláusula, conforme mostrado no exemplo a seguir:

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

Se desejar executar operações de consulta adicionais em cada grupo, você pode especificar um identificador temporário usando a palavra-chave contextual into . Ao usar intoo , você deve continuar com a consulta e, eventualmente, terminá-la com uma select instrução ou outra group cláusula, conforme mostrado no trecho a seguir:

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

Exemplos mais completos do uso de group com e sem into são fornecidos na seção Exemplo deste artigo.

Enumerando os resultados de uma consulta de grupo

Como os IGrouping<TKey,TElement> objetos produzidos por uma group consulta são essencialmente uma lista de listas, você deve usar um loop foreach aninhado para acessar os itens em cada grupo. O loop externo itera sobre as chaves de grupo, e o loop interno itera sobre cada item no próprio grupo. Um grupo pode ter uma chave, mas não elementos. A seguir está o foreach loop que executa a consulta nos exemplos 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-chave

As chaves de grupo podem ser de qualquer tipo, como uma cadeia de caracteres, um tipo numérico interno ou um tipo nomeado definido pelo usuário ou um tipo anônimo.

Agrupamento por cadeia de caracteres

Os exemplos de código anteriores usavam um chararquivo . Uma chave de cadeia de caracteres poderia facilmente ter sido especificada, por exemplo, o sobrenome 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;

Agrupamento por bool

O exemplo a seguir mostra o uso de um valor bool para uma chave para dividir os resultados em dois grupos. Observe que o valor é produzido por uma subexpressão na 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
*/

Agrupamento por intervalo numérico

O próximo exemplo usa uma expressão para criar chaves de grupo numérico que representam um intervalo de percentis. Observe o uso de let como um local conveniente para armazenar um resultado de chamada de método, para que você não precise chamar o método duas vezes na group cláusula. Para obter mais informações sobre como usar métodos com segurança em expressões de consulta, consulte Manipular exceções em expressões 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 {0} and {1}", temp, 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
 */

Agrupamento por chaves compostas

Use uma chave composta quando quiser agrupar elementos de acordo com mais de uma chave. Você cria uma chave composta usando um tipo anônimo ou um tipo nomeado para manter o elemento de chave. No exemplo a seguir, suponha que uma classe Person foi declarada com membros nomeados surname e city. A group cláusula faz com que um grupo separado seja criado para cada conjunto de pessoas com o mesmo sobrenome e a mesma cidade.

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

Use um tipo nomeado se você precisar passar a variável de consulta para outro método. Crie uma classe especial usando propriedades implementadas automaticamente para as chaves e, em seguida, substitua os Equals métodos and GetHashCode . Você também pode usar um struct, caso em que você não precisa substituir estritamente esses métodos. Para obter mais informações, consulte Como implementar uma classe leve com propriedades implementadas automaticamente e Como consultar arquivos duplicados em uma árvore de diretório. O último artigo tem um exemplo de código que demonstra como usar uma chave composta com um tipo nomeado.

Exemplo 1

O exemplo a seguir mostra o padrão padrão para ordenar os dados de origem em grupos quando nenhuma lógica de consulta adicional é aplicada aos grupos. Isso é chamado de agrupamento sem continuação. Os elementos em uma matriz de cadeias de caracteres são agrupados de acordo com sua primeira letra. O resultado da consulta é um IGrouping<TKey,TElement> tipo que contém uma propriedade pública Key do tipo char e uma IEnumerable<T> coleção que contém cada item no agrupamento.

O resultado de uma group cláusula é uma sequência de sequências. Portanto, para acessar os elementos individuais dentro de cada grupo retornado, use um loop aninhado foreach dentro do loop que itera as chaves de grupo, conforme mostrado no exemplo a seguir.

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

Exemplo 2

Este exemplo mostra como executar lógica adicional nos grupos depois de criá-los, usando uma continuação com into. Para obter mais informações, consulte em. O exemplo a seguir consulta cada grupo para selecionar apenas aqueles cujo valor-chave é uma vogal.

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

Observações

No momento da compilação, group as cláusulas são traduzidas em chamadas para o GroupBy método.

Consulte também