group – klauzule (Referenční dokumentace jazyka C#)
Klauzule group
vrátí sekvenci IGrouping<TKey,TElement> objektů, které obsahují nula nebo více položek, které odpovídají hodnotě klíče skupiny. Můžete například seskupit posloupnost řetězců podle prvního písmene v každém řetězci. V tomto případě je první písmeno klíčem a má znak typu a je uloženo ve Key
vlastnosti každého IGrouping<TKey,TElement> objektu. Kompilátor odvodí typ klíče.
Výraz dotazu můžete ukončit klauzulí group
, jak je znázorněno v následujícím příkladu:
// Query variable is an IEnumerable<IGrouping<char, Student>>
var studentQuery1 =
from student in students
group student by student.Last[0];
Pokud chcete u každé skupiny provádět další operace dotazů, můžete zadat dočasný identifikátor pomocí klíčového slova into contextual. Při použití příkazu into
musíte pokračovat v dotazu a nakonec ho ukončit příkazem select
nebo jinou group
klauzulí, jak je znázorněno v následujícím výňatku:
// 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;
Podrobnější příklady použití group
s a bez into
jsou uvedeny v části Příklad tohoto článku.
Vytvoření výčtu výsledků skupinového dotazu
IGrouping<TKey,TElement> Vzhledem k tomu, že objekty vytvořené dotazem group
jsou v podstatě seznamem seznamů, musíte pro přístup k položkám v každé skupině použít vnořenou smyčku foreach. Vnější smyčka iteruje nad klíči skupiny a vnitřní smyčka iteruje nad každou položkou v samotné skupině. Skupina může mít klíč, ale žádné prvky. Následuje smyčka foreach
, která spustí dotaz v předchozích příkladech kódu:
// 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 klíčů
Skupinové klíče můžou být libovolného typu, například řetězec, předdefinovaný číselný typ nebo uživatelem definovaný pojmenovaný typ nebo anonymní typ.
Seskupení podle řetězce
Předchozí příklady kódu používaly char
. Místo toho by se dal snadno zadat řetězcový klíč, například celé příjmení:
// 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;
Seskupování podle bool
Následující příklad ukazuje použití logické hodnoty klíče k rozdělení výsledků do dvou skupin. Všimněte si, že hodnota je vytvořená dílčím výrazem v klauzuli group
.
class GroupSample1
{
// The element type of the data source.
public class Student
{
public string First { get; set; }
public string Last { get; set; }
public int ID { get; set; }
public 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 List<Student>
{
new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 72, 81, 60}},
new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {99, 89, 91, 95}},
new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {72, 81, 65, 84}},
new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {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());
}
}
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* 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
*/
Seskupení podle číselného rozsahu
V dalším příkladu se pomocí výrazu vytvoří klíče číselné skupiny, které představují rozsah percentilu. Všimněte si použití funkce let jako vhodného umístění pro uložení výsledku volání metody, abyste nemuseli v group
klauzuli volat metodu dvakrát. Další informace o tom, jak bezpečně používat metody ve výrazech dotazů, najdete v tématu Zpracování výjimek ve výrazech dotazů.
class GroupSample2
{
// The element type of the data source.
public class Student
{
public string First { get; set; }
public string Last { get; set; }
public int ID { get; set; }
public 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 List<Student>
{
new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 72, 81, 60}},
new Student {First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {99, 89, 91, 95}},
new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {72, 81, 65, 84}},
new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {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());
}
}
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* 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
*/
Seskupení podle složených klíčů
Složený klíč použijte, pokud chcete seskupit prvky podle více než jednoho klíče. Složený klíč vytvoříte pomocí anonymního typu nebo pojmenovaného typu, který bude obsahovat prvek klíče. V následujícím příkladu předpokládejme, že byla deklarována třída Person
se členy s názvem surname
a city
. Klauzule group
způsobí vytvoření samostatné skupiny pro každou skupinu osob se stejným příjmením a stejným městem.
group person by new {name = person.surname, city = person.city};
Pokud potřebujete předat proměnnou dotazu jiné metodě, použijte pojmenovaný typ. Vytvořte speciální třídu pomocí automaticky implementovaných vlastností klíčů a potom přepište Equals metody a GetHashCode . Můžete také použít strukturu, v takovém případě není nutné tyto metody přepsat. Další informace najdete v tématech Jak implementovat odlehčenou třídu s automaticky implementovanými vlastnostmi a Dotazování na duplicitní soubory v adresářovém stromu. Druhý článek obsahuje příklad kódu, který ukazuje, jak použít složený klíč s pojmenovaným typem.
Příklad 1
Následující příklad ukazuje standardní vzor pro řazení zdrojových dat do skupin, pokud se na skupiny nepoužívá žádná další logika dotazu. Říká se tomu seskupení bez pokračování. Prvky v poli řetězců jsou seskupeny podle jejich prvního písmene. Výsledkem dotazu je IGrouping<TKey,TElement> typ, který obsahuje veřejnou Key
vlastnost typu char
a IEnumerable<T> kolekci, která obsahuje každou položku v seskupení.
Výsledkem klauzule group
je posloupnost sekvencí. Proto pro přístup k jednotlivým prvkům v rámci každé vrácené skupiny použijte uvnitř smyčky vnořenou foreach
smyčku, která iteruje klíče skupiny, jak je znázorněno v následujícím příkladu.
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);
}
}
// Keep the console window open in debug mode
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* 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
*/
Příklad 2
Tento příklad ukazuje, jak u skupin po jejich vytvoření provést další logiku pomocí pokračování s into
. Další informace najdete v tématu. Následující příklad dotazuje každou skupinu a vybere pouze ty, jejichž hodnotou klíče je samohláska.
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);
}
}
// Keep the console window open in debug mode
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* 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
*/
Poznámky
V době kompilace se group
klauzule překládají na GroupBy volání metody .