Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
A group cláusula retorna uma sequência de IGrouping<TKey,TElement> objetos. A sequência contém zero ou mais itens que correspondem ao valor da chave para o grupo. Por exemplo, é possível agrupar uma sequência de cadeias de caracteres de acordo com a primeira letra de cada cadeia de caracteres. Nesse caso, a primeira letra é a chave e tem um tipo char. Cada IGrouping<TKey,TElement> objeto armazena essa chave em sua Key propriedade. O compilador infere o tipo da chave.
A linguagem C# faz referência a documentos da versão mais recentemente lançada da linguagem C#. Ele também contém a documentação inicial para funcionalidades em pré-visualizações públicas para o próximo lançamento do idioma.
A documentação identifica qualquer recurso introduzido pela primeira vez nas três últimas versões do idioma ou nas versões prévias públicas atuais.
Dica
Para descobrir quando um recurso foi introduzido pela primeira vez em C#, consulte o artigo sobre o histórico de versão da linguagem C#.
É possível finalizar uma expressão de consulta com uma cláusula group, 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 você quiser executar operações de consulta adicionais em cada grupo, especifique um identificador temporário usando a palavra-chave contextual. Ao usar into, é necessário continuar a consulta e, em algum momento, finalizá-la com uma instrução select ou outra cláusula group, 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 serão apresentados na seção Exemplo deste artigo.
Enumerando os resultados de uma consulta de grupo
Como os IGrouping<TKey,TElement> objetos que uma group consulta produz 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 nas chaves de grupo e o loop interno itera em cada item do grupo em si. Um grupo pode ter uma chave, mas nenhum elemento. O loop a seguir foreach 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 de chave
As chaves de grupo podem ser de qualquer tipo, como uma cadeia de caracteres, um tipo numérico interno, um tipo nomeado definido pelo usuário ou um tipo anônimo.
Agrupar por cadeia de caracteres
Os exemplos de código anteriores usaram um char. Você também pode especificar uma chave de cadeia de caracteres, como 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;
Agrupar por bool
O exemplo a seguir usa um valor bool para uma chave para dividir os resultados em dois grupos. O valor vem de 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
*/
Agrupar por alcance numérico
O próximo exemplo usa uma expressão para criar chaves de grupo numéricas que representam um intervalo de percentil. Ele usa let 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, confira Como tratar exceções nas 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 {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
*/
Agrupando por chaves compostas
Use uma chave composta quando quiser agrupar elementos por mais de uma chave. Crie uma chave composta usando um tipo anônimo ou um tipo nomeado para manter o elemento chave. No exemplo a seguir, suponha que uma classe Person tenha membros nomeados surname e city. A group cláusula cria um grupo separado 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 precisar passar a variável de consulta para outro método. Crie uma classe especial com propriedades implementadas automaticamente para as chaves e, em seguida, substitua os métodos e GetHashCode os Equals métodos. Você também pode usar um struct, que não exige estritamente essas substituições de método. 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 apresenta um exemplo de código que demonstra como usar uma chave composta com um tipo nomeado.
Exemplos
O exemplo a seguir mostra o padrão padrão para ordenar dados de origem em grupos quando você não aplica nenhuma lógica de consulta extra aos grupos. Esse padrão é chamado de agrupamento sem uma continuação. O exemplo agrupa os elementos em uma matriz de cadeias de caracteres de acordo com sua primeira letra. O resultado da consulta é um tipo IGrouping<TKey,TElement> que contém uma propriedade Key pública do tipo char e uma coleção IEnumerable<T> que contém cada item no agrupamento.
O resultado de uma cláusula group é uma sequência de sequências. Para acessar os elementos individuais em 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 '{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
*/
O exemplo a seguir mostra como executar uma lógica extra nos grupos depois de criá-los usando uma continuação com into. Para obter mais informações, consulte into. O exemplo a seguir consulta cada grupo para selecionar apenas aqueles cujo valor da 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: {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
*/
Em tempo de compilação, o compilador converte cláusulas group em chamadas para o GroupBy método.
A sintaxe da consulta de group cláusula não dá suporte ao comparador de igualdade personalizado. Se você quiser usar IEqualityComparer em sua consulta, use explicitamente o GroupBy método.