group — Klauzula (odwołanie w C#)
Klauzula group
zwraca sekwencję IGrouping<TKey,TElement> obiektów, które zawierają zero lub więcej elementów pasujących do wartości klucza dla grupy. Można na przykład zgrupować sekwencję ciągów zgodnie z pierwszą literą w każdym ciągu. W tym przypadku pierwsza litera jest kluczem i ma typ char i jest przechowywana we Key
właściwości każdego IGrouping<TKey,TElement> obiektu. Kompilator wywnioskuje typ klucza.
Wyrażenie zapytania można zakończyć za pomocą klauzuli group
, jak pokazano w poniższym przykładzie:
// Query variable is an IEnumerable<IGrouping<char, Student>>
var studentQuery1 =
from student in students
group student by student.Last[0];
Jeśli chcesz wykonać dodatkowe operacje zapytań dla każdej grupy, możesz określić identyfikator tymczasowy przy użyciu elementu w kontekstowym słowie kluczowym. Jeśli używasz metody into
, musisz kontynuować wykonywanie zapytania i ostatecznie zakończyć je instrukcją lub inną group
klauzuląselect
, jak pokazano na poniższym fragcie:
// 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;
Bardziej kompletne przykłady użycia z elementem group
i bez into
znajdują się w sekcji Przykład w tym artykule.
Wyliczanie wyników zapytania grupy
IGrouping<TKey,TElement> Ponieważ obiekty tworzone przez group
zapytanie są zasadniczo listą list, należy użyć zagnieżdżonej pętli foreach, aby uzyskać dostęp do elementów w każdej grupie. Pętla zewnętrzna iteruje klucze grupy, a pętla wewnętrzna iteruje każdy element w samej grupie. Grupa może mieć klucz, ale nie ma żadnych elementów. Poniżej znajduje się pętla foreach
, która wykonuje zapytanie w poprzednich przykładach kodu:
// 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);
}
}
Typy kluczy
Klucze grupy mogą być dowolnym typem, takim jak ciąg, wbudowany typ liczbowy lub typ nazwany zdefiniowany przez użytkownika lub typ anonimowy.
Grupowanie według ciągu
W poprzednich przykładach kodu użyto elementu char
. Zamiast tego można było łatwo określić klucz ciągu, na przykład pełne nazwisko:
// 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;
Grupowanie według wartości logicznej
W poniższym przykładzie pokazano użycie wartości logicznej klucza w celu podzielenia wyników na dwie grupy. Należy pamiętać, że wartość jest generowany przez wyrażenie podrzędne w klauzuli 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
*/
Grupowanie według zakresu liczbowego
W następnym przykładzie użyto wyrażenia do utworzenia liczbowych kluczy grup reprezentujących zakres percentylu. Zwróć uwagę na użycie funkcji let jako wygodnej lokalizacji do przechowywania wyniku wywołania metody, aby nie trzeba było wywołać metody dwa razy w klauzuli group
. Aby uzyskać więcej informacji na temat bezpiecznego używania metod w wyrażeniach zapytań, zobacz Obsługa wyjątków w wyrażeniach zapytań.
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
*/
Grupowanie według kluczy złożonych
Użyj klucza złożonego, jeśli chcesz zgrupować elementy według więcej niż jednego klucza. Klucz złożony tworzy się przy użyciu typu anonimowego lub nazwanego typu do przechowywania elementu klucza. W poniższym przykładzie przyjęto założenie, że klasa Person
została zadeklarowana przy użyciu elementów członkowskich o nazwach surname
i city
. Klauzula group
powoduje utworzenie oddzielnej grupy dla każdego zestawu osób o tym samym imieniu i tym samym mieście.
group person by new {name = person.surname, city = person.city};
Użyj nazwanego typu, jeśli musisz przekazać zmienną kwerendy do innej metody. Utwórz klasę specjalną przy użyciu automatycznie zaimplementowanych właściwości kluczy, a następnie przesłoń Equals metody i GetHashCode . Można również użyć struktury, w tym przypadku nie trzeba ściśle zastępować tych metod. Aby uzyskać więcej informacji, zobacz Jak zaimplementować lekką klasę z automatycznie zaimplementowanymi właściwościami i Jak wykonywać zapytania dotyczące zduplikowanych plików w drzewie katalogów. W tym drugim artykule przedstawiono przykład kodu, który pokazuje, jak używać klucza złożonego z nazwanym typem.
Przykład 1
W poniższym przykładzie przedstawiono standardowy wzorzec porządkowania danych źródłowych w grupach, gdy do grup nie jest stosowana żadna dodatkowa logika zapytania. Jest to nazywane grupowaniem bez kontynuacji. Elementy w tablicy ciągów są grupowane zgodnie z ich pierwszą literą. Wynikiem zapytania jest IGrouping<TKey,TElement> typ zawierający właściwość publiczną Key
typu char
i IEnumerable<T> kolekcję zawierającą każdy element w grupowaniu.
Wynikiem klauzuli group
jest sekwencja sekwencji. W związku z tym, aby uzyskać dostęp do poszczególnych elementów w każdej zwróconej grupie, użyj zagnieżdżonej foreach
pętli wewnątrz pętli, która iteruje klucze grupy, jak pokazano w poniższym przykładzie.
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
*/
Przykład 2
W tym przykładzie pokazano, jak wykonać dodatkową logikę dla grup po ich utworzeniu przy użyciu kontynuacji z into
. Aby uzyskać więcej informacji, zobacz. Poniższy przykład wysyła zapytanie do każdej grupy, aby wybrać tylko te, których wartość klucza jest samogłoską.
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
*/
Uwagi
W czasie group
kompilacji klauzule są tłumaczone na wywołania GroupBy metody .
Porównanie równości niestandardowej nie jest obsługiwane w składni zapytania klauzuli group
. Użyj GroupBy metody jawnie, jeśli chcesz użyć IEqualityComparer w zapytaniu.