CodeGym /Kurslar /C# SELF /LINQ-un yeni metodları: Co...

LINQ-un yeni metodları: CountByAggregateBy

C# SELF
Səviyyə , Dərs
Mövcuddur

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ə)
"Neonvill"
3 3
"Rouzvoter"
1 1
"Los Santos"
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ı
GroupBy + Select + Count
students.CountBy(s => s.City)
Şəhərlərə görə qiymətlərin cəmi
GroupBy + Select + Sum
students.AggregateBy(..., 0, ...)
Şəhərlərə görə maksimum qiymət
GroupBy + Select + Max
students.AggregateBy(..., ...)
Sətirdə simvolların tezliyi
GroupBy + Select + Count
word.CountBy(ch => ch)

İşdə və real layihələrdə praktik fayda

Belə metodlar kimi CountByAggregateBy 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.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION