1. Giriş
Əgər sən nə vaxtsa "klassik" LINQ-sorğusu yazmısansa, məsələn, qruplaşdırma ilə:
var cityCounts = students.GroupBy(s => s.City)
.Select(g => new { City = g.Key, Count = g.Count() });
yəqin ki, sadə qruplara görə say üçün kodun bir az uzun olduğunu hiss etmisən. .NET 9 çıxanda Microsoft komandası dev-lərin həyatını asanlaşdırmaq üçün LINQ-a iki çox lazım olan metod əlavə etdi:
- CountBy — elementləri açar üzrə tez saymaq üçün rahat yol.
- AggregateBy — açar üzrə universal aqreqator (təkcə saymır, həm də toplama və s. edir).
Yeri gəlmişkən, bu metodlar community-dən çoxlu sorğulara görə əlavə olunub və onların analoqları çoxdan bir çox "pro" LINQ kitabxanalarında, məsələn, MoreLINQ-da var.
2. CountBy metodu: qruplara görə qısa say
Təsvir
CountBy — sözün əsl mənasında "qruplaşdır, sonra dərhal say". Metod özü "açar-say" cütlükləri kolleksiyası qaytarır, bu isə real data analizində tez-tez lazım olur: top şəhərlər, qiymətlərə görə tələbə sayı, nəyinsə tezliyi və s.
İmza (sadələşdirilmiş):
IEnumerable<(TKey Key, int Count)> CountBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
Praktikada istifadə
Nümunə 1: Şəhərlərə görə tələbə sayı
Güman edək ki, belə bir class-ımız var:
class Student
{
public string Name { get; set; }
public int Grade { get; set; }
public string City { get; set; }
}
Və app-ımızda tələbə siyahısı:
var students = new List<Student>
{
new Student { Name = "İvan", Grade = 5, City = "Neonvill" },
new Student { Name = "Anna", Grade = 4, City = "Los-Santos" },
new Student { Name = "Egor", Grade = 3, City = "Neonvill" },
new Student { Name = "Mariya", Grade = 5, City = "Rouzvoter" },
new Student { Name = "Oleg", Grade = 4, City = "Neonvill" }
};
.NET 8 və əvvəlki versiyalarda belə yazmalı idin:
var group = students.GroupBy(s => s.City)
.Select(g => new { City = g.Key, Count = g.Count() });
foreach (var cityInfo in group)
{
Console.WriteLine($"{cityInfo.City}: {cityInfo.Count} tələbə");
}
.NET 9-da isə bunu bir qısa metod həll edir:
var cityCounts = students.CountBy(s => s.City);
foreach (var (city, count) in cityCounts)
{
Console.WriteLine($"{city}: {count} tələbə");
}
Bəli, düz görürsən — heç bir əl ilə GroupBy yoxdur, Count() hesablaması da yoxdur — hər şey sadə və birbaşa!
Nümunə 2: Qiymətlərə görə say
Hər dekanın əsas sualı — qrupda neçə əlaçı, neçə üçlükçü var:
var gradeCounts = students.CountBy(s => s.Grade);
foreach (var (grade, count) in gradeCounts)
{
Console.WriteLine($"Qiymət {grade}: {count} tələbə");
}
Nümunə 3: Sətirdə simvolların tezliyi
CountBy təkcə obyektlərə yox, daha sadə şeylərə də işləyir:
string word = "supercalifragilisticexpialidocious";
var charFrequencies = word.CountBy(ch => ch);
foreach (var (letter, count) in charFrequencies)
{
Console.WriteLine($"{letter} : {count}");
}
Bu yaddaşda necə görünür (şəkil)
| Açar | Say (CountBy-dan əvvəl) | Say (CountBy ilə) |
|---|---|---|
|
3 | 3 |
|
1 | 1 |
|
1 | 1 |
Bu yanaşma kodu oxunaqlı edir və qruplaşdırmada səhvlərin sayını azaldır.
Tipik səhvlər və nüanslar
Unutma ki, CountBy (Key, Count) tuple qaytarır, anonim tip yox. Bəzən bu bir az semantik az görünə bilər, amma tam şəffafdır. Həmçinin, Count üçün hansı tip olacağını birbaşa seçmək olmur — həmişə int qaytarır.
3. AggregateBy metodu: universal aqreqatlar qruplara görə
Təsvir
Əgər CountBy sadə saydırsa, AggregateBy — əsl İsveçrə bıçağıdır! Satıcıya görə satışların cəmini, sinfə görə maksimum qiyməti və ya ümumiyyətlə accumulator ilə ifadə olunan istənilən şeyi hesablamaq istəyirsən? Bunu AggregateBy edir.
İmza (çox sadələşdirilmiş):
IEnumerable<(TKey Key, TAccumulate Result)> AggregateBy<TSource, TKey, TAccumulate>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
TAccumulate seed,
Func<TAccumulate, TSource, TAccumulate> func)
- keySelector — nəyə görə qruplaşdırmaq.
- seed — accumulator üçün ilkin dəyər.
- func — nəticəni necə "yığmaq" lazım olduğunu göstərən funksiya.
Praktikada istifadə
Nümunə 1: Şəhərlərə görə qiymətlərin cəmi
Belə bir nəhəng LINQ əvəzinə:
var sums = students.GroupBy(s => s.City)
.Select(g => new { City = g.Key, Sum = g.Sum(s => s.Grade) });
Belə yazırsan:
var sums = students.AggregateBy(
s => s.City, // şəhərə görə qruplaşdırırıq
0, // ilkin cəm
(acc, s) => acc + s.Grade // qiyməti əlavə edirik
);
foreach (var (city, sum) in sums)
{
Console.WriteLine($"{city}: qiymətlərin cəmi = {sum}");
}
Nümunə 2: Şəhərlərə görə maksimum qiymət
Maksimum dəyər istəyirsənsə — accumulator-u dəyiş:
var maxByCity = students.AggregateBy(
s => s.City,
int.MinValue, // ilkin dəyər — mümkün minimum
(max, s) => Math.Max(max, s.Grade)
);
foreach (var (city, maxGrade) in maxByCity)
{
Console.WriteLine($"{city}: maksimum qiymət = {maxGrade}");
}
Nümunə 3: Adları sətirə yığmaq
Tələbə siyahımızı yeniləyirik:
var namesByCity = students.AggregateBy(
s => s.City,
"", // ilkin sətir
(acc, s) => string.IsNullOrEmpty(acc) ? s.Name : acc + ", " + s.Name
);
foreach (var (city, names) in namesByCity)
{
Console.WriteLine($"{city}: {names}");
}
Daxildən görünüş (sxem)
. {Student, Student, Student, ...}
|
| (city-ə görə qruplaşdırırıq)
v
["Neonvill"] ["Rouzvoter"] ["Los Santos"]
| | |
| (akkumulyasiya) |
|___________________________|
|
v
{("Neonvill", cəm), ("Rouzvoter", cəm), ...}
Köhnə üsullarla müqayisə
Əvvəllər belə tapşırıqlar üçün həmişə GroupBy + Select yazırdıq, içində də Sum, Aggregate və ya başqa bir şey. İndi AggregateBy bu "əziyyəti" gizlədir və kodu qısa, ifadəli edir.
Praktika: app-da tətbiq edirik
Tədris layihəmiz — tələbə gündəliyi. Orada şəhərlərə görə orta bal hesabatı əlavə edirik.
// Orta balı AggregateBy ilə hesablayaq — cəmi və sayı yığırıq, sonra orta tapırıq
var avgByCity = students.AggregateBy(
s => s.City,
(Sum: 0, Count: 0),
(acc, s) => (acc.Sum + s.Grade, acc.Count + 1)
).Select(x => (x.Key, Average: (double)x.Result.Sum / x.Result.Count));
foreach (var (city, avg) in avgByCity)
{
Console.WriteLine($"{city}: orta bal = {avg:F2}");
}
Burada cəmi və sayı tuple-da yığırıq, sonra cəmi sayına bölüb orta tapırıq.
Xüsusiyyətlər və tələlər
Əgər accumulator-un referans tipdirsə, unutma: AggregateBy içində bir qrup üçün eyni referans istifadə olunur, yəni obyekti dəyişsən, təsadüfən bütün qrupun nəticəsinə təsir edə bilərsən.
Və bir də: təkcə qruplaşdırma yox, nəticəni dəyişmək də lazımdırsa — dərhal .Select istifadə et. Semantik olaraq uyğun seed (ilkin dəyər) seçməyə çalış, yoxsa gözlənilməz nəticə ala bilərsən (məsələn, sətirlər üçün 0 istifadə etmə).
4. Köhnə və yeni yanaşmanın müqayisəsi
| Tapşırıq | Köhnə LINQ | Yeni LINQ .NET 9 |
|---|---|---|
| Şəhərlərə görə tələbə sayı | |
|
| Şəhərlərə görə qiymətlərin cəmi | |
|
| Şəhərlərə görə maksimum qiymət | |
|
| Sətirdə simvolların tezliyi | |
|
İşdə və real layihələrdə praktik fayda
Belə metodlar kimi CountBy və AggregateBy statistik hesabatlar yazanda, vizuallaşdırma üçün data hazırlayanda, pivot cədvəllər quranda və digər tipik tapşırıqlarda əvəzolunmaz olur. Kod qısalır, oxunaqlı olur və ən əsası — daha etibarlı: qruplaşdırma və aqreqasiya ilə əl işi az, səhv ehtimalı da az.
Junior/Middle vakansiyasında müsahibədə yeni LINQ metodlarını bilmək göstərir ki, sən platformanın inkişafını izləyirsən, prod-da isə öz "velosipedini" icad etməyə ehtiyac yoxdur — oxunaqlı və rahat konstruksiyalardan istifadə edirsən, onları başa düşmək və dəstəkləmək asandır.
GO TO FULL VERSION