Gegevens groeperen (C#)
Groeperen verwijst naar de werking van het indelen van gegevens in groepen, zodat de elementen in elke groep een gemeenschappelijk kenmerk delen. In de volgende afbeelding ziet u de resultaten van het groeperen van een reeks tekens. De sleutel voor elke groep is het teken.
Belangrijk
In deze voorbeelden wordt een System.Collections.Generic.IEnumerable<T> gegevensbron gebruikt. Gegevensbronnen op System.Linq.IQueryProvider basis van het gebruik van System.Linq.IQueryable<T> gegevensbronnen en expressiestructuren. Expressiestructuren hebben beperkingen voor de toegestane C#-syntaxis. Bovendien kan elke IQueryProvider
gegevensbron, zoals EF Core , meer beperkingen opleggen. Raadpleeg de documentatie voor uw gegevensbron.
De standaardqueryoperatormethoden waarmee gegevenselementen worden gegroepeerd, worden vermeld in de volgende tabel.
Methodenaam | Beschrijving | Syntaxis van C#-queryexpressie | Meer informatie |
---|---|---|---|
GroupBy | Hiermee worden elementen gegroepeerd die een gemeenschappelijk kenmerk delen. Een IGrouping<TKey,TElement> object vertegenwoordigt elke groep. | group … by – of – group … by … into … |
Enumerable.GroupBy Queryable.GroupBy |
ToLookup | Voegt elementen in een Lookup<TKey,TElement> (een-op-veel-woordenlijst) in op basis van een sleutelkiezerfunctie. | Niet van toepassing. | Enumerable.ToLookup |
In het volgende codevoorbeeld wordt de group by
component gebruikt om gehele getallen in een lijst te groeperen op basis van of ze even of oneven zijn.
List<int> numbers = [35, 44, 200, 84, 3987, 4, 199, 329, 446, 208];
IEnumerable<IGrouping<int, int>> query = from number in numbers
group number by number % 2;
foreach (var group in query)
{
Console.WriteLine(group.Key == 0 ? "\nEven numbers:" : "\nOdd numbers:");
foreach (int i in group)
{
Console.WriteLine(i);
}
}
De equivalente query met behulp van de methodesyntaxis wordt weergegeven in de volgende code:
List<int> numbers = [35, 44, 200, 84, 3987, 4, 199, 329, 446, 208];
IEnumerable<IGrouping<int, int>> query = numbers
.GroupBy(number => number % 2);
foreach (var group in query)
{
Console.WriteLine(group.Key == 0 ? "\nEven numbers:" : "\nOdd numbers:");
foreach (int i in group)
{
Console.WriteLine(i);
}
}
Notitie
In de volgende voorbeelden in dit artikel worden de algemene gegevensbronnen voor dit gebied gebruikt.
Elk Student
heeft een cijferniveau, een primaire afdeling en een reeks scores. Een Teacher
heeft ook een City
eigenschap die de campus identificeert waar de docent klassen heeft. A Department
heeft een naam en een verwijzing naar een Teacher
persoon die als afdelingshoofd fungeert.
U vindt de voorbeeldgegevensset in de bronopslagplaats.
public enum GradeLevel
{
FirstYear = 1,
SecondYear,
ThirdYear,
FourthYear
};
public class Student
{
public required string FirstName { get; init; }
public required string LastName { get; init; }
public required int ID { get; init; }
public required GradeLevel Year { get; init; }
public required List<int> Scores { get; init; }
public required int DepartmentID { get; init; }
}
public class Teacher
{
public required string First { get; init; }
public required string Last { get; init; }
public required int ID { get; init; }
public required string City { get; init; }
}
public class Department
{
public required string Name { get; init; }
public int ID { get; init; }
public required int TeacherID { get; init; }
}
Queryresultaten groeperen
Groeperen is een van de krachtigste mogelijkheden van LINQ. In de volgende voorbeelden ziet u hoe u gegevens op verschillende manieren kunt groeperen:
- Met één eigenschap.
- Door de eerste letter van een tekenreekseigenschap.
- Door een berekend numeriek bereik.
- Op booleaanse predicaat of andere expressie.
- Door een samengestelde sleutel.
Bovendien projecteren de laatste twee query's hun resultaten in een nieuw anoniem type dat alleen de voor- en familienaam van de leerling/student bevat. Zie de groepscomponent voor meer informatie.
Voorbeeld van groeperen op één eigenschap
In het volgende voorbeeld ziet u hoe u bronelementen groeperen met behulp van één eigenschap van het element als groepssleutel. De sleutel is een enum
, het jaar van de student op school. De groeperingsbewerking maakt gebruik van de standaard gelijkheidsvergelijker voor het type.
var groupByYearQuery =
from student in students
group student by student.Year into newGroup
orderby newGroup.Key
select newGroup;
foreach (var yearGroup in groupByYearQuery)
{
Console.WriteLine($"Key: {yearGroup.Key}");
foreach (var student in yearGroup)
{
Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
}
}
De equivalente code met behulp van methodesyntaxis wordt weergegeven in het volgende voorbeeld:
// Variable groupByLastNamesQuery is an IEnumerable<IGrouping<string,
// DataClass.Student>>.
var groupByYearQuery = students
.GroupBy(student => student.Year)
.OrderBy(newGroup => newGroup.Key);
foreach (var yearGroup in groupByYearQuery)
{
Console.WriteLine($"Key: {yearGroup.Key}");
foreach (var student in yearGroup)
{
Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
}
}
Voorbeeld van groeperen op waarde
In het volgende voorbeeld ziet u hoe u bronelementen groeperen met behulp van iets anders dan een eigenschap van het object voor de groepssleutel. In dit voorbeeld is de sleutel de eerste letter van de familienaam van de leerling/student.
var groupByFirstLetterQuery =
from student in students
let firstLetter = student.LastName[0]
group student by firstLetter;
foreach (var studentGroup in groupByFirstLetterQuery)
{
Console.WriteLine($"Key: {studentGroup.Key}");
foreach (var student in studentGroup)
{
Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
}
}
Geneste foreach is vereist voor toegang tot groepsitems.
De equivalente code met behulp van methodesyntaxis wordt weergegeven in het volgende voorbeeld:
var groupByFirstLetterQuery = students
.GroupBy(student => student.LastName[0]);
foreach (var studentGroup in groupByFirstLetterQuery)
{
Console.WriteLine($"Key: {studentGroup.Key}");
foreach (var student in studentGroup)
{
Console.WriteLine($"\t{student.LastName}, {student.FirstName}");
}
}
Groeperen op een bereikvoorbeeld
In het volgende voorbeeld ziet u hoe u bronelementen groeperen met behulp van een numeriek bereik als groepssleutel. De query projecteert vervolgens de resultaten in een anoniem type dat alleen de voor- en familienaam en het percentielbereik waartoe de student behoort, bevat. Een anoniem type wordt gebruikt omdat het niet nodig is om het volledige Student
object te gebruiken om de resultaten weer te geven. GetPercentile
is een helperfunctie waarmee een percentiel wordt berekend op basis van de gemiddelde score van de student. De methode retourneert een geheel getal tussen 0 en 10.
static int GetPercentile(Student s)
{
double avg = s.Scores.Average();
return avg > 0 ? (int)avg / 10 : 0;
}
var groupByPercentileQuery =
from student in students
let percentile = GetPercentile(student)
group new
{
student.FirstName,
student.LastName
} by percentile into percentGroup
orderby percentGroup.Key
select percentGroup;
foreach (var studentGroup in groupByPercentileQuery)
{
Console.WriteLine($"Key: {studentGroup.Key * 10}");
foreach (var item in studentGroup)
{
Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
}
}
Geneste foreach is vereist om groepen en groepitems te herhalen. De equivalente code met behulp van methodesyntaxis wordt weergegeven in het volgende voorbeeld:
static int GetPercentile(Student s)
{
double avg = s.Scores.Average();
return avg > 0 ? (int)avg / 10 : 0;
}
var groupByPercentileQuery = students
.Select(student => new { student, percentile = GetPercentile(student) })
.GroupBy(student => student.percentile)
.Select(percentGroup => new
{
percentGroup.Key,
Students = percentGroup.Select(s => new { s.student.FirstName, s.student.LastName })
})
.OrderBy(percentGroup => percentGroup.Key);
foreach (var studentGroup in groupByPercentileQuery)
{
Console.WriteLine($"Key: {studentGroup.Key * 10}");
foreach (var item in studentGroup.Students)
{
Console.WriteLine($"\t{item.LastName}, {item.FirstName}");
}
}
Voorbeeld van groeperen op vergelijking
In het volgende voorbeeld ziet u hoe u bronelementen groeperen met behulp van een Boole-vergelijkingsexpressie. In dit voorbeeld test de Boole-expressie of de gemiddelde examenscore van een student groter is dan 75. Net als in eerdere voorbeelden worden de resultaten geprojecteerd in een anoniem type omdat het volledige bronelement niet nodig is. De eigenschappen in het anonieme type worden eigenschappen van het Key
lid.
var groupByHighAverageQuery =
from student in students
group new
{
student.FirstName,
student.LastName
} by student.Scores.Average() > 75 into studentGroup
select studentGroup;
foreach (var studentGroup in groupByHighAverageQuery)
{
Console.WriteLine($"Key: {studentGroup.Key}");
foreach (var student in studentGroup)
{
Console.WriteLine($"\t{student.FirstName} {student.LastName}");
}
}
De equivalente query met behulp van de methodesyntaxis wordt weergegeven in de volgende code:
var groupByHighAverageQuery = students
.GroupBy(student => student.Scores.Average() > 75)
.Select(group => new
{
group.Key,
Students = group.AsEnumerable().Select(s => new { s.FirstName, s.LastName })
});
foreach (var studentGroup in groupByHighAverageQuery)
{
Console.WriteLine($"Key: {studentGroup.Key}");
foreach (var student in studentGroup.Students)
{
Console.WriteLine($"\t{student.FirstName} {student.LastName}");
}
}
Groeperen op anoniem type
In het volgende voorbeeld ziet u hoe u een anoniem type gebruikt om een sleutel in te kapselen die meerdere waarden bevat. In dit voorbeeld is de eerste sleutelwaarde de eerste letter van de familienaam van de leerling/student. De tweede sleutelwaarde is een Booleaanse waarde die aangeeft of de student meer dan 85 heeft gescoord op het eerste examen. U kunt de groepen op elke eigenschap in de sleutel bestellen.
var groupByCompoundKey =
from student in students
group student by new
{
FirstLetterOfLastName = student.LastName[0],
IsScoreOver85 = student.Scores[0] > 85
} into studentGroup
orderby studentGroup.Key.FirstLetterOfLastName
select studentGroup;
foreach (var scoreGroup in groupByCompoundKey)
{
var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
foreach (var item in scoreGroup)
{
Console.WriteLine($"\t{item.FirstName} {item.LastName}");
}
}
De equivalente query met behulp van de methodesyntaxis wordt weergegeven in de volgende code:
var groupByCompoundKey = students
.GroupBy(student => new
{
FirstLetterOfLastName = student.LastName[0],
IsScoreOver85 = student.Scores[0] > 85
})
.OrderBy(studentGroup => studentGroup.Key.FirstLetterOfLastName);
foreach (var scoreGroup in groupByCompoundKey)
{
var s = scoreGroup.Key.IsScoreOver85 ? "more than 85" : "less than 85";
Console.WriteLine($"Name starts with {scoreGroup.Key.FirstLetterOfLastName} who scored {s}");
foreach (var item in scoreGroup)
{
Console.WriteLine($"\t{item.FirstName} {item.LastName}");
}
}
Een geneste groep maken
In het volgende voorbeeld ziet u hoe u geneste groepen maakt in een LINQ-queryexpressie. Elke groep die wordt gemaakt op basis van het niveau studentenjaar of cijfer wordt vervolgens verder onderverdeeld in groepen op basis van de namen van de personen.
var nestedGroupsQuery =
from student in students
group student by student.Year into newGroup1
from newGroup2 in
from student in newGroup1
group student by student.LastName
group newGroup2 by newGroup1.Key;
foreach (var outerGroup in nestedGroupsQuery)
{
Console.WriteLine($"DataClass.Student Level = {outerGroup.Key}");
foreach (var innerGroup in outerGroup)
{
Console.WriteLine($"\tNames that begin with: {innerGroup.Key}");
foreach (var innerGroupElement in innerGroup)
{
Console.WriteLine($"\t\t{innerGroupElement.LastName} {innerGroupElement.FirstName}");
}
}
}
Er zijn drie geneste foreach
lussen vereist om de binnenste elementen van een geneste groep te herhalen.
(Beweeg de muisaanwijzer over de iteratievariabelen, outerGroup
en innerGroup
innerGroupElement
om het werkelijke type te zien.)
De equivalente query met behulp van de methodesyntaxis wordt weergegeven in de volgende code:
var nestedGroupsQuery =
students
.GroupBy(student => student.Year)
.Select(newGroup1 => new
{
newGroup1.Key,
NestedGroup = newGroup1
.GroupBy(student => student.LastName)
});
foreach (var outerGroup in nestedGroupsQuery)
{
Console.WriteLine($"DataClass.Student Level = {outerGroup.Key}");
foreach (var innerGroup in outerGroup.NestedGroup)
{
Console.WriteLine($"\tNames that begin with: {innerGroup.Key}");
foreach (var innerGroupElement in innerGroup)
{
Console.WriteLine($"\t\t{innerGroupElement.LastName} {innerGroupElement.FirstName}");
}
}
}
Een subquery uitvoeren op een groeperingsbewerking
In dit artikel worden twee verschillende manieren beschreven om een query te maken waarmee de brongegevens in groepen worden geordeld en vervolgens een subquery voor elke groep afzonderlijk wordt uitgevoerd. De basistechniek in elk voorbeeld is om de bronelementen te groeperen met behulp van een vervolg met de naam newGroup
en vervolgens een nieuwe subquery te genereren op newGroup
basis van . Deze subquery wordt uitgevoerd voor elke nieuwe groep die door de buitenste query is gemaakt. In dit specifieke voorbeeld is de uiteindelijke uitvoer geen groep, maar een platte reeks anonieme typen.
Zie de groepscomponent voor meer informatie over het groeperen. Zie voor meer informatie over vervolgen. In het volgende voorbeeld wordt een in-memory gegevensstructuur gebruikt als de gegevensbron, maar dezelfde principes zijn van toepassing op elk type LINQ-gegevensbron.
var queryGroupMax =
from student in students
group student by student.Year into studentGroup
select new
{
Level = studentGroup.Key,
HighestScore = (
from student2 in studentGroup
select student2.Scores.Average()
).Max()
};
var count = queryGroupMax.Count();
Console.WriteLine($"Number of groups = {count}");
foreach (var item in queryGroupMax)
{
Console.WriteLine($" {item.Level} Highest Score={item.HighestScore}");
}
De query in het voorgaande codefragment kan ook worden geschreven met behulp van de syntaxis van de methode. Het volgende codefragment heeft een semantisch equivalente query die is geschreven met behulp van de syntaxis van de methode.
var queryGroupMax =
students
.GroupBy(student => student.Year)
.Select(studentGroup => new
{
Level = studentGroup.Key,
HighestScore = studentGroup.Max(student2 => student2.Scores.Average())
});
var count = queryGroupMax.Count();
Console.WriteLine($"Number of groups = {count}");
foreach (var item in queryGroupMax)
{
Console.WriteLine($" {item.Level} Highest Score={item.HighestScore}");
}