Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Klauzula group zwraca sekwencję IGrouping<TKey,TElement> obiektów. Sekwencja zawiera zero lub więcej elementów, które pasują 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. Każdy IGrouping<TKey,TElement> obiekt przechowuje ten klucz we właściwości Key . Kompilator wywnioskuje typ klucza.
Dokumentacja języka C# zawiera ostatnio wydaną wersję języka C#. Zawiera również początkową dokumentację dla funkcjonalności w publicznych wersjach testowych nadchodzącego wydania języka.
Dokumentacja identyfikuje dowolną funkcję po raz pierwszy wprowadzoną w ostatnich trzech wersjach języka lub w bieżącej publicznej wersji zapoznawczej.
Wskazówka
Aby dowiedzieć się, kiedy funkcja została po raz pierwszy wprowadzona w języku C#, zapoznaj się z artykułem dotyczącym historii wersji języka C#.
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, określ identyfikator tymczasowy przy użyciu elementu w kontekstowym słowie kluczowym. 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 group przez 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 ma żadnych elementów.
foreach Poniższa pętla 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. Możesz również określić klucz ciągu, taki jak 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 użyto wartości logicznej klucza, aby podzielić wyniki na dwie grupy. Wartość pochodzi z wyrażenia podrzędnego 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. Używa let metody do przechowywania wyniku wywołania metody, więc nie trzeba 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. Utwórz klucz złożony 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 ma elementy członkowskie o nazwie surname i city. Klauzula group tworzy oddzielną grupę 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ę z automatycznie zaimplementowanymi właściwościami kluczy, a następnie przesłoń Equals metody i GetHashCode . Można również użyć struktury, która nie wymaga ściśle tych przesłonięć 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łady
Poniższy przykład przedstawia standardowy wzorzec porządkowania danych źródłowych w grupach, gdy nie stosujesz żadnej dodatkowej logiki zapytań do grup. Ten wzorzec jest nazywany grupowaniem bez kontynuacji. W przykładzie elementy są grupowane w tablicy ciągów zgodnie z 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. 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
*/
W poniższym przykładzie pokazano, jak wykonać dodatkową logikę w grupach po ich utworzeniu przy użyciu kontynuacji z into. Aby uzyskać więcej informacji, zobacz into. 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
*/
W czasie kompilacji kompilator tłumaczy klauzule group na wywołania GroupBy metody .
Składnia zapytania klauzuli nie obsługuje niestandardowego group porównywania równości. Jeśli chcesz użyć IEqualityComparer w zapytaniu, jawnie użyj GroupBy metody .