Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Предложение group возвращает последовательность IGrouping<TKey,TElement> объектов. Последовательность содержит ноль или несколько элементов, которые соответствуют значению ключа для группы. Например, можно сгруппировать последовательность строк в соответствии с первой буквой в каждой строке. В этом случае первая буква является ключом и имеет символ типа. Каждый IGrouping<TKey,TElement> объект хранит этот ключ в своем свойстве Key . Тип ключа определяется компилятором.
Справочные документы по языку C# описывают последнюю выпущенную версию языка C#. Она также содержит начальную документацию по функциям в общедоступных предварительных версиях для предстоящего языкового выпуска.
Документация определяет любую функцию, впервые представленную в последних трех версиях языка или в текущих общедоступных предварительных версиях.
Подсказка
Чтобы узнать, когда функция впервые появилась в C#, ознакомьтесь со статьей об истории версий языка C#.
Вы можете завершить выражение запроса предложением group, как показано в следующем примере:
// Query variable is an IEnumerable<IGrouping<char, Student>>
var studentQuery1 =
from student in students
group student by student.Last[0];
Если требуется выполнить дополнительные операции запроса для каждой группы, укажите временный идентификатор с помощью контекстного ключевого слова. При использовании ключевого слова into необходимо продолжить запрос и завершить его инструкцией select или другим предложением group, как показано в следующем фрагменте:
// 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;
Более полные примеры использования group с into и без into приведены в разделе "Примеры" этой статьи.
Перечисление результатов группового запроса
IGrouping<TKey,TElement> Так как объекты, создаваемые group запросом, по сути являются списком списков, необходимо использовать вложенный цикл foreach для доступа к элементам в каждой группе. Во внешнем цикле выполняется итерация по ключам групп, а во внутреннем цикле — по каждому элементу в самой группе. У группы может быть ключ, но нет элементов.
foreach Следующий цикл выполняет запрос в предыдущих примерах кода:
// 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);
}
}
Типы ключей
Ключи групп могут быть любого типа, например строкового, встроенного числового, пользовательского именованного или анонимного типа.
Группировка по строке
В предыдущих примерах кода использовался ключ типа char. Можно также указать строковый ключ, например полное фамилию:
// 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;
Группировка по булеву значению
В следующем примере используется логическое значение ключа для разделения результатов на две группы. Значение поступает из под-выражения в предложении group .
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
*/
Группировка по числовому диапазону
В следующем примере с помощью выражения создаются числовые ключи групп, обозначающие диапазоны значений в выборке. Он используется let для хранения результата вызова метода, поэтому вам не нужно дважды вызывать метод в предложении group . См. сведения о безопасном использовании методов в выражениях запросов в руководстве по обработке исключений в выражениях запросов.
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
*/
Группировка по составному ключу
Используйте составной ключ, если вы хотите сгруппировать элементы по нескольким ключам. Создайте составной ключ с помощью анонимного типа или именованного типа для хранения элемента ключа. В следующем примере предположим, что класс Person имеет члены с именем surname и city. Предложение group создает отдельную группу для каждого набора лиц с одинаковым фамилией и тем же городом.
group person by new {name = person.surname, city = person.city};
Используйте именованный тип, если необходимо передать переменную запроса другому методу. Создайте специальный класс с автоматически реализованными свойствами для ключей, а затем переопределите Equals методы и GetHashCode методы. Можно также использовать структуру, которая не требует строго переопределения этих методов. Дополнительные сведения см. в статье "Как реализовать упрощенный класс с автоматически реализованными свойствами и как запрашивать повторяющиеся файлы в дереве каталогов". В последней статье имеется пример кода, демонстрирующий использование составных ключей с именованным типом.
Примеры
В следующем примере показан стандартный шаблон упорядочивания исходных данных в группы, если к группам не применяется дополнительная логика запроса. Этот шаблон называется группированием без продолжения. Пример группирует элементы в массиве строк в соответствии с их первой буквой. Результатом запроса является тип IGrouping<TKey,TElement>, который содержит открытое свойство Key типа char и коллекцию IEnumerable<T>, содержащую каждый элемент в группе.
Результатом выполнения предложения group является последовательность из последовательностей. Чтобы получить доступ к отдельным элементам в каждой возвращаемой группе, используйте вложенный foreach цикл внутри цикла, который выполняет итерацию ключей группы, как показано в следующем примере.
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
*/
В следующем примере показано, как выполнить дополнительную логику для групп после их создания с помощью продолженияinto. Дополнительные сведения см. в разделе into. В следующем примере выполняется запрос, выбирающий только те группы, ключевое значение которых является гласной.
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
*/
Во время компиляции компилятор преобразует group предложения в вызовы GroupBy метода.
Синтаксис запроса предложения не поддерживает пользовательское group сравнение равенства. Если вы хотите использовать IEqualityComparer в запросе GroupBy , явно используйте этот метод.