Adatok csoportosítása (C#)
A csoportosítás az adatok csoportokba helyezésének műveletére utal, így az egyes csoportok elemei közös attribútumban osztoznak. Az alábbi ábrán egy karaktersorozat csoportosításának eredményei láthatók. Az egyes csoportok kulcsa a karakter.
Az adatelemeket csoportosító szabványos lekérdezésoperátor-metódusok az alábbi táblázatban találhatók.
Metódus neve | Leírás | C# lekérdezési kifejezés szintaxisa | További információ |
---|---|---|---|
GroupBy | Csoportosítja azokat az elemeket, amelyek közös attribútummal rendelkeznek. Az IGrouping<TKey,TElement> objektumok az egyes csoportokat jelölik. | group … by -vagy- group … by … into … |
Enumerable.GroupBy Queryable.GroupBy |
ToLookup | Elemek beszúrása egy Lookup<TKey,TElement> (egy-a-többhöz) szótárba egy kulcsválasztó függvény alapján. | Nem alkalmazható. | Enumerable.ToLookup |
Az alábbi példakód a group by
záradék használatával csoportosítja az egész számokat egy listában attól függően, hogy párosak vagy páratlanok-e.
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);
}
}
A metódusszintaxissal egyenértékű lekérdezés a következő kódban jelenik meg:
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);
}
}
A cikkben szereplő alábbi példák a terület közös adatforrásait használják:
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; }
}
Mindegyiknek Student
van egy osztályszintje, egy elsődleges osztálya és egy sor pontszáma. Az A-nek Teacher
is van egy City
tulajdonsága, amely azonosítja azt a campust, ahol a tanár órákat tart. Az A-nek Department
van egy neve, és egy olyan személyre Teacher
való hivatkozás, aki az osztályvezető.
Lekérdezési eredmények csoportosítása
A csoportosítás a LINQ egyik leghatékonyabb képessége. Az alábbi példák bemutatják, hogyan csoportosíthatja az adatokat különböző módokon:
- Egyetlen tulajdonság alapján.
- Egy sztringtulajdonság első betűje szerint.
- Számított numerikus tartomány szerint.
- Logikai predikátum vagy más kifejezés szerint.
- Összetett kulccsal.
Emellett az utolsó két lekérdezés az eredményeket egy új névtelen típusba veti, amely csak a tanuló vezeték- és családnevét tartalmazza. További információt a csoport záradékában talál.
Csoportosítás egyetlen tulajdonság alapján – példa
Az alábbi példa bemutatja, hogyan csoportosíthatja a forráselemeket úgy, hogy az elem egyetlen tulajdonságát használja csoportkulcsként. A kulcs egy enum
, a tanulói év az iskolában. A csoportosítási művelet a típus alapértelmezett egyenlőség-összehasonlítóját használja.
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}");
}
}
A metódusszintaxissal egyenértékű kód az alábbi példában látható:
// 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}");
}
}
Példa érték szerinti csoportosításra
Az alábbi példa bemutatja, hogyan csoportosíthatja a forráselemeket a csoportkulcs objektumának tulajdonságától eltérő tulajdonság használatával. Ebben a példában a kulcs a tanuló családi nevének első betűje.
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}");
}
}
A csoportelemek eléréséhez beágyazott foreach szükséges.
A metódusszintaxissal egyenértékű kód az alábbi példában látható:
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}");
}
}
Csoportosítás tartomány szerint – példa
Az alábbi példa bemutatja, hogyan csoportosíthatja a forráselemeket numerikus tartomány használatával csoportkulcsként. A lekérdezés ezután névtelen típusba alakítja az eredményeket, amely csak az utó- és családnevet, valamint azt a percentilistartományt tartalmazza, amelyhez a tanuló tartozik. A rendszer névtelen típust használ, mert nem szükséges a teljes Student
objektumot használni az eredmények megjelenítéséhez. GetPercentile
egy segédfüggvény, amely a tanuló átlagos pontszáma alapján számít ki egy percentiliset. A metódus 0 és 10 közötti egész számot ad vissza.
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}");
}
}
A csoportok és csoportelemek iterálásához beágyazott foreach szükséges. A metódusszintaxissal egyenértékű kód az alábbi példában látható:
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}");
}
}
Csoportosítás összehasonlítási példa alapján
Az alábbi példa bemutatja, hogyan csoportosíthatja a forráselemeket logikai összehasonlító kifejezéssel. Ebben a példában a logikai kifejezés azt vizsgálja, hogy egy tanuló átlagos vizsgaeredménye nagyobb-e, mint 75. A korábbi példákhoz hasonlóan az eredmények névtelen típusba lesznek vetítve, mivel a teljes forráselemre nincs szükség. A névtelen típusú tulajdonságok tulajdonságokká válnak a Key
tagon.
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}");
}
}
A metódusszintaxissal egyenértékű lekérdezés a következő kódban jelenik meg:
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}");
}
}
Csoportosítás névtelen típus szerint
Az alábbi példa bemutatja, hogyan használhat névtelen típust egy több értéket tartalmazó kulcs beágyazásához. Ebben a példában az első kulcsérték a tanuló családi nevének első betűje. A második kulcsérték egy logikai érték, amely meghatározza, hogy a tanuló több mint 85 pontot ért-e el az első vizsgán. A csoportokat a kulcs bármely tulajdonsága alapján rendezheti.
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}");
}
}
A metódusszintaxissal egyenértékű lekérdezés a következő kódban jelenik meg:
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}");
}
}
Beágyazott csoport létrehozása
Az alábbi példa bemutatja, hogyan hozhat létre beágyazott csoportokat egy LINQ-lekérdezési kifejezésben. A tanulói év vagy osztályzat szerint létrehozott csoportokat ezután tovább osztják csoportokra az egyének neve alapján.
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}");
}
}
}
A beágyazott csoportok belső elemein való iteráláshoz három beágyazott foreach
hurok szükséges.
(Vigye az egérmutatót az iterációs változók fölé, outerGroup
innerGroup
és innerGroupElement
tekintse meg a tényleges típusukat.)
A metódusszintaxissal egyenértékű lekérdezés a következő kódban jelenik meg:
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}");
}
}
}
Al lekérdezés végrehajtása csoportosítási műveleten
Ez a cikk két különböző módszert mutat be egy olyan lekérdezés létrehozására, amely csoportokba rendezi a forrásadatokat, majd egyenként hajt végre al lekérdezéseket az egyes csoportokon. Az egyes példák alapvető technikája a forráselemek csoportosítása egy nevesített newGroup
folytatással, majd egy új alquery létrehozása.newGroup
Ezt az al lekérdezést a külső lekérdezés által létrehozott új csoportokon futtatja a rendszer. Ebben a példában a végső kimenet nem csoport, hanem névtelen típusok egybesimított sorozata.
A csoportosításról további információt a csoport záradékában talál. További információ a folytatásokról: Az alábbi példa egy memórián belüli adatstruktúrát használ adatforrásként, de ugyanezek az alapelvek vonatkoznak bármilyen LINQ-adatforrásra.
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}");
}
Az előző kódrészlet lekérdezése metódusszintaxissal is írható. A következő kódrészlet egy szemantikailag egyenértékű lekérdezéssel rendelkezik, amely metódusszintaxissal íródott.
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}");
}
Lásd még
Visszajelzés
https://aka.ms/ContentUserFeedback.
Hamarosan elérhető: 2024-ben fokozatosan kivezetjük a GitHub-problémákat a tartalom visszajelzési mechanizmusaként, és lecseréljük egy új visszajelzési rendszerre. További információ:Visszajelzés küldése és megtekintése a következőhöz: