1. Giriş
Məlumatların qruplaşdırılması — bu, kolleksiyadakı elementləri ümumi əlamətə görə "səbətlərə" bölməkdir. Təsəvvür elə ki, almaları rənginə görə ayırırsan: bütün qırmızıları bir səbətə, yaşıl olanları başqa bir səbətə, sarıları isə başqa yerə qoyursan. Proqramlaşdırmada buna qruplaşdırma deyilir.
Nəyə lazımdır?
Real tapşırıqlarda tez-tez insanları şəhərə görə, məhsulları kateqoriyaya görə, tranzaksiyaları tarixə görə qruplaşdırmaq faydalı olur. Bu, faydalı analitika aparmağa imkan verir: məsələn, hər sinifdə neçə tələbə var, ya da hansı şəhərlər ən çox tələbəyə sahibdir.
LINQ bunun üçün xüsusi bir metod verir: GroupBy. Həmçinin query syntax-da da rahat analoqu var — bu da group by-dır. Gəlin bu prosesi nümunələrlə bizim tədris tətbiqimizdə — tələbə və siniflərin uçotu sistemində baxaq.
2. GroupBy method syntax-da
Əsas imza və qaytardığı nəticə
GroupBy metodu bir az qorxulu görünür:
IEnumerable<IGrouping<TKey, TElement>> GroupBy<TElement, TKey>(
this IEnumerable<TElement> source,
Func<TElement, TKey> keySelector
)
Burada:
- source — ilkin kolleksiya (məsələn, tələbələrin siyahısı).
- keySelector — qruplaşdırma əlamətini müəyyən edən funksiya, məsələn, ClassName və ya City property-si.
Vacibdir!
Nəticə — sadəcə obyektlərin massivi deyil, xüsusi qrupların kolleksiyasıdır (IGrouping<TKey, TElement>). Hər bir qrupda var:
- Qrupun açarı (Key — elementləri birləşdirən şey),
- Bu qrupa düşən elementlərin kolleksiyası.
Sxem:
| Açar (Key) | Qrupun elementləri (obyekt qrupu) |
|---|---|
| "9A" | İvan, Oleq, Mariya |
| "10B" | Svetlana, Pyotr |
| ... | ... |
Nümunə 1: Tələbələri sinif adına görə qruplaşdırırıq
Tutaq ki, bizdə Student klası var:
public class Student
{
public string Name { get; set; }
public string ClassName { get; set; }
public int Grade { get; set; }
}
Və tələbələrin kolleksiyası:
var students = new List<Student>
{
new Student { Name = "İvan", ClassName = "9A", Grade = 5 },
new Student { Name = "Oleq", ClassName = "9A", Grade = 4 },
new Student { Name = "Mariya", ClassName = "10B", Grade = 5 },
new Student { Name = "Svetlana", ClassName = "10B", Grade = 3 }
};
Tələbələri sinifə görə qruplaşdırırıq:
var studentsByClass = students.GroupBy(s => s.ClassName);
foreach (var group in studentsByClass)
{
Console.WriteLine($"Sinif: {group.Key}"); // Qrupun açarı
foreach (var student in group)
{
Console.WriteLine($" - {student.Name}, qiymət: {student.Grade}");
}
}
Nə baş verir?
GroupBy metodu bizə iki qrup yaratdı: biri "9A" üçün, digəri isə "10B" üçün. Qrupları gəzərək hər "dəstə" barədə istənilən aggregat məlumatı çıxara bilərik.
Vizualizasiya: GroupBy-dan sonra kolleksiya necə görünür
Bunu belə təsəvvür elə:
students.GroupBy(s => s.ClassName)
├─ qrup "9A" { İvan, Oleq }
└─ qrup "10B" { Mariya, Svetlana }
Hər "budaq" — açara görə birləşdirilmiş tələbələrin ayrıca mini-siyahısıdır.
Tələbələrə görə qiymət: istənilən əlamətə görə qruplaşdırma
Gəlin bu dəfə sinifə yox, qiymətə görə qruplaşdıraq!
var studentsByGrade = students.GroupBy(s => s.Grade);
foreach (var group in studentsByGrade)
{
Console.WriteLine($"Qiymət: {group.Key}");
foreach (var student in group)
{
Console.WriteLine($" - {student.Name} sinifdən {student.ClassName}");
}
}
Çıxış:
Qiymət: 5
- İvan sinifdən 9A
- Mariya sinifdən 10B
Qiymət: 4
- Oleq sinifdən 9A
Qiymət: 3
- Svetlana sinifdən 10B
3. LINQ-da qrupların işlənməsi: GroupBy-dan sonra nə etmək olar
Çox vaxt qruplaşdırmadan sonra sadəcə qrupları çıxarmaq yox, hansısa aggregat məlumat almaq istəyirik — məsələn, hər qrupda neçə element var, orta balı tapmaq, adların siyahısını toplamaq və s.
Nümunə 2: Hər sinifdə tələbələrin sayını hesablamaq
var classCounts = students
.GroupBy(s => s.ClassName)
.Select(g => new { Class = g.Key, Count = g.Count() });
foreach (var cc in classCounts)
{
Console.WriteLine($"Sinifdə {cc.Class} — {cc.Count} tələbə var");
}
Nəticə:
Sinifdə 9A — 2 tələbə var
Sinifdə 10B — 2 tələbə var
- Əvvəlcə tələbələri qruplara böldük.
- Sonra hər qrup üçün açar (sinif adı) və say (metod Count()) ilə anonim obyekt yaratdıq.
Nümunə 3: Sinifə görə əlaçıların adlarını toplamaq
var excellentByClass = students
.Where(s => s.Grade == 5)
.GroupBy(s => s.ClassName)
.Select(g => new
{
Class = g.Key,
ExcellentStudents = g.Select(s => s.Name).ToList()
});
foreach (var group in excellentByClass)
{
Console.WriteLine($"Sinifdə {group.Class} əlaçılar: {string.Join(", ", group.ExcellentStudents)}");
}
4. group by query syntax-da
Əgər SQL-ə bənzər nəsə istəyirsənsə, ya da bazalar dünyasından gəlmisənsə, LINQ group by ... into ... tipli sorğu sintaksisini dəstəkləyir.
Qruplaşdırmanın əsas nümunəsi
var studentsByClass =
from s in students
group s by s.ClassName into classGroup
select classGroup;
foreach (var group in studentsByClass)
{
Console.WriteLine($"Sinif: {group.Key}");
foreach (var student in group)
{
Console.WriteLine($" - {student.Name}");
}
}
Əvvəlcə əlamətə görə "qruplaşdırırıq", sonra qrupa ad veririk (into classGroup) — və sonra istədiyimizi edə bilərik.
Aggregatlarla proyeksiya
Tutaq ki, şəhərləri və hər şəhərdən neçə tələbə olduğunu bilmək istəyirik:
var countsByCity =
from s in students
group s by s.ClassName into classGroup
select new { Class = classGroup.Key, Count = classGroup.Count() };
foreach (var item in countsByCity)
{
Console.WriteLine($"Sinifdə {item.Class} — {item.Count} tələbə var");
}
— Sintaksis eynidir, amma select-dən sonra istənilən proyeksiya (aggregasiya, siyahılar, orta qiymətlər və s.) etmək olar.
5. Əlavə ssenarilər
Bir neçə əlamətə görə qruplaşdırma (mürəkkəb açar)
Bəzən bir neçə property-nin kombinasiyasına görə qruplaşdırmaq lazımdır. Məsələn, sinif və qiymətə görə:
var byClassAndGrade = students
.GroupBy(s => new { s.ClassName, s.Grade });
foreach (var group in byClassAndGrade)
{
Console.WriteLine($"Sinif: {group.Key.ClassName}, Qiymət: {group.Key.Grade}");
foreach (var student in group)
{
Console.WriteLine($" - {student.Name}");
}
}
Qrupun açarı indi — anonim tipdir (bir neçə sahənin kombinasiyası).
Qruplar praktikada necə işləyir?
LINQ-un bir az daxili mətbəxi
- GroupBy edəndə, LINQ daxilində xüsusi bir "cədvəl" qurur: hər unikal açar üçün ayrıca qrup.
- Qruplar IGrouping<TKey, TElement> interfeysini reallaşdırır. Qruplaşdırdığın əlamətin dəyərini almaq üçün Key property-sinə baxa bilərsən.
Lazy evaluation və performans
- Qruplar dərhal hesablanmır, yalnız onları gəzəndə (foreach) yaranır. Yəni, çox böyük kolleksiyalar olsa belə — qruplar "yaradılmır", ta ki, onlara müraciət edəsən, onlar yalnız istifadə zamanı mövcuddur.
- Əgər qruplara dəfələrlə baxmaq planın varsa — .ToList() və ya .ToArray() çağırıb nəticəni "sabitləşdirə" bilərsən.
Praktiki tətbiq
Qruplaşdırma mürəkkəb hesabatlar, statistika və analitika qurmağa imkan verir. Budur, harada lazım ola bilər:
- Online mağazada: sifarişləri istifadəçiyə görə qruplaşdırıb hər kəsin alış tarixçəsini göstərmək.
- Təhsil platformasında: tələbələri müəllimə və ya fənnə görə qruplaşdırmaq.
- HR tətbiqində: işçiləri departamentə görə qruplaşdırıb headcount hesablamaq.
- Müsahibədə: real məlumatlarla qruplaşdırma və aggregasiya yazmağı istəyə bilərlər.
LINQ belə tapşırıqları çox tez prototipləşdirməyə imkan verir, qruplaşdırmaları oxumaq və yazmaq bacarığı isə hər .NET developer üçün böyük üstünlükdür.
Vizual sxem
Budur, GroupBy metodu ilə kolleksiyanın işlənməsinin sadə blok-sxemi:
[Tələbələrin kolleksiyası]
|
[ClassName-ə görə GroupBy]
|
[Qrup "9A"] ---> [İvan, Oleq]
[Qrup "10B"] --> [Mariya, Svetlana]
Hər qrup — açarı olan ayrıca mini-kolleksiyadır.
6. Tez-tez edilən səhvlər və tələlər
Bəzi tələbələr GroupBy-ın nəticəsinin spesifikliyinə görə əvvəlcə çaşırlar:
- GroupBy çağırandan sonra sadəcə List<List<Student>> tipli "qruplar" yox, IGrouping<TKey, TElement> kolleksiyası alırsan. Qruplarla işləmək üçün Key property-sindən və qrupun elementlərini gəzərək istifadə et.
- Mürəkkəb açara (bir neçə sahə) görə qruplaşdıranda: anonim tipdən istifadə etməyi və ya ayrıca class yaratmağı unutma, əgər strukturlaşdırılmış açar lazımdırsa.
- Qruplarda yalnız elementlərin sayını bilmək lazımdırsa — Select(g => g.Count()) istifadə etməyi unutma, yoxsa hər qrupun içində elementləri əl ilə saymalı olacaqsan.
- Bəzən dövrələrin iç-içə olmasında çaşmaq olar: birinci dövr qruplar üzrə, ikinci isə qrupun elementləri üzrə.
- Lambda və ya açarla səhv etsən, gözlədiyin qrupları yox, başqa qruplar (və ya hamısı bir böyük qrup) ala bilərsən.
GO TO FULL VERSION