Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
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, używając kontekstowego słowa kluczowego into. Jeśli używasz into
, musisz kontynuować zapytanie i ostatecznie zakończyć je instrukcją select
lub inną klauzulą group
, jak pokazano na poniższym fragmencie.
// 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;
Kompletne przykłady użycia z elementem group
i bez into
znajdują się w sekcji Przykłady 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 po kluczach grupy, a pętla wewnętrzna iteruje po elementach w samej grupie. Grupa może mieć klucz, ale nie mieć ż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, typ nazwany zdefiniowany przez użytkownika czy 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 (bool) jako klucza do podzielenia wyników na dwie grupy. Należy pamiętać, że wartość jest generowana 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 {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
*/
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 specjalną klasę, używając automatycznie zaimplementowanych właściwości dla kluczy, a następnie przesłoń metody Equals 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 '{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, korzystając z kontynuacji przy 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: {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
*/
Uwagi
Podczas kompilacji klauzule group
są tłumaczone na wywołania metody GroupBy.
Niestandardowy porównywacz równości nie jest obsługiwany w składni zapytania w klauzuli group
. Użyj GroupBy metody jawnie, jeśli chcesz użyć IEqualityComparer w zapytaniu.