Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Предложение 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 , явно используйте этот метод.