CodeGym /Kurslar /C# SELF /LINQ-də Union, Intersect, Except əməliyyatları

LINQ-də Union, Intersect, Except əməliyyatları

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

1. Giriş

Çox maraqlı və praktikada tez-tez istifadə olunan bir mövzuya keçirik: LINQ-də çoxluq əməliyyatları — Union, Intersect, Except. Bunlar kolleksiyalarla elə işləməyə imkan verir ki, sanki əsl çoxluq algebrası ilə məşğulsan (və ya proqramçının stolunda iki paketdəki stikerləri sayırsan — razılaş, demək olar ki, eynidir). Əgər heç vaxt çoxluq algebrası eşitməmisənsə (və ya yadından çıxıbsa), bu qorxulu səslənə bilər. Amma əslində bu çox sadə bir anlayışdır: təsəvvür elə iki torba meyvə var. Çoxluq algebrası — bu, başa düşmək üsuludur ki, hansı meyvələr hər iki torbada var, hansılar yalnız birindədir, hansılar isə birləşdirəndə hamısı olur. Bu, stikerlərlə oyun kimidir: hamısını bir yerə yığa bilərsən, ortaq olanları tapa bilərsən və ya birindən digərlərini çıxara bilərsən. Vəssalam.

Praktika: Bu metodları istifadə etmək rahatdır, məsələn, iki fərqli sorğunun nəticələrini birləşdirmək, ortaq elementləri tapmaq və ya kolleksiyalar arasındakı fərqi müəyyən etmək lazım olanda. Məsələn:

  • Satış siyahısında və ya alış siyahısında rast gəlinən bütün məhsulların ümumi siyahısını tərtib etmək.
  • Hər iki siyahıda olan məhsulları tapmaq — ortaq mövqelər.
  • Və ya müəyyən etmək ki, anbarda olan, amma alınmayan məhsullar hansılardır — kimsə orada köhnəlib!

Real tapşırıqlar: Çoxluq algebrası hər yerdədir: istifadəçiləri abunələrə görə filtrləmək, müxtəlif qruplarda olanları tapmaq, unikal və ya kəsişən sifarişləri tapmaq, iki fərqli sorğunun nəticələrini müqayisə etmək və sairə.

2. Union əməliyyatı — kolleksiyaların birləşdirilməsi

Hər şey sadədir, ona görə dərhal praktikaya keçək. Tutaq ki, iki məhsul siyahımız var:

List<string> warehouseProducts = new List<string> { "Süd", "Çörək", "Pendir", "Yumurta" };
List<string> recentlySold = new List<string> { "Çörək", "Pendir", "Salami", "Çay" };

Union nə edir?

Union unikal elementləri qaytarır, hansı ki, ən azı bir kolleksiyada var.
Yəni, bu — birləşdirmədir.

var allProducts = warehouseProducts.Union(recentlySold);

foreach (var product in allProducts)
{
    Console.WriteLine(product);
}
// Çıxacaq: Süd, Çörək, Pendir, Yumurta, Salami, Çay

Bu vizual olaraq belə görünür:

Anbar Satılıb Union (Birləşmə)
Süd Çörək Süd
Çörək Pendir Çörək
Pendir Salami Pendir
Yumurta Çay Yumurta
Salami
Çay

Çoxluq algebrası baxımından, Union — bu "və ya" əməliyyatıdır: mənə hər yerdə olanı ver. İki qutu fərqli çay növləri təsəvvür elə — əgər sən bütün dadları yoxlamaq istəsən, fərqi yoxdur, hansısa çay təkrarlanırsa — onu yalnız bir dəfə içəcəksən.

Union avtomatik olaraq dublikatları silir (element tipinin Equals və GetHashCode metodlarının implementasiyasına əsaslanır). Əgər öz class-ların üçün birləşdirmə edirsənsə (məsələn, Product), bu metodların düzgün yazıldığına əmin ol, yoxsa Union qəribə işləyəcək: eyni elementlər fərqli kimi qəbul oluna bilər!

Unutma: nəticə kolleksiyasında elementlərin sırası birinci kolleksiyadan saxlanılır, yeni elementlər isə (ikinci kolleksiyada ilk dəfə rast gəlinənlər) sona əlavə olunur.

Obyektlərlə nümunə

Bizim "Mağaza" tətbiqimizə davam edirik. İki Product siyahımız var:

public class Product
{
    public string Name { get; set; }
    public string Category { get; set; }

    // Union/Intersect/Except-in düzgün işləməsi üçün düzgün Equals və GetHashCode lazımdır!
    public override bool Equals(object? obj) =>
        obj is Product other && Name == other.Name && Category == other.Category;

    public override int GetHashCode() => HashCode.Combine(Name, Category);
}

List<Product> stock = new()
{
    new Product { Name = "Süd", Category = "Süd məhsulları" },
    new Product { Name = "Çörək", Category = "Çörək məmulatları" },
};

List<Product> sold = new()
{
    new Product { Name = "Çörək", Category = "Çörək məmulatları" },
    new Product { Name = "Salami", Category = "Kolbasalar" },
};

var all = stock.Union(sold);

// Diqqət yetir: elementlər təkrarlanmır,
// hətta "görünüşcə" eynidirlərsə də, məntiqə görə bərabər olmalıdırlar. 

foreach (var p in all)
    Console.WriteLine($"{p.Name} ({p.Category})");

Tipik səhv: Əgər Equals/GetHashCode override olunmayıbsa, Union eyni dəyərlərə malik fərqli obyektləri fərqli elementlər kimi qəbul edəcək!

3. Intersect əməliyyatı — kolleksiyaların kəsişməsi

Intersect qaytarır o elementləri ki, bütün başlanğıc kolleksiyalarda var. Bu, çoxluq algebrasında "və" kimidir.

Nümunə

Əvvəlki nümunədəki siyahıları xatırla:

var commonProducts = warehouseProducts.Intersect(recentlySold);
foreach (var product in commonProducts)
{
    Console.WriteLine(product);
}
// Çıxacaq: Çörək, Pendir

Vizualizasiya:

Anbar Satılıb Intersect (Ortaq)
Çörək Çörək Çörək
Pendir Pendir Pendir

Harada lazım olur?

  • Anbarında olan hansı məhsulların bu gün satıldığını bilmək istəyirsən.
  • Müsahibələrdə tez-tez soruşurlar: "İki siyahının kəsişməsini necə tapmaq olar?" — LINQ bunu bir sətrə həll edir!
  • Biznes tətbiqlərində kəsişmə tez-tez bir neçə əlamətə görə filtr üçün lazımdır.

Xüsusiyyətlər və tipik səhvlər

Əgər element kolleksiyada bir neçə dəfə varsa, nəticədə yalnız bir dəfə olacaq.
Mürəkkəb obyektlər üçün (məsələn, Product class-ı) yenə də Equals/GetHashCode düzgün yazılması vacibdir.

Obyektlərlə nümunə

var commonObjects = stock.Intersect(sold);
foreach (var p in commonObjects)
    Console.WriteLine($"{p.Name} ({p.Category})");
// Çıxacaq yalnız Çörək (Çörək məmulatları)

4. Except əməliyyatı — kolleksiyaların fərqi

Except qaytarır o elementləri ki, yalnız birinci kolleksiyada var, amma ikinci kolleksiyada yoxdur.

Nümunə

var productsOnlyInStock = warehouseProducts.Except(recentlySold);
foreach (var product in productsOnlyInStock)
{
    Console.WriteLine(product);
}
// Çıxacaq: Süd, Yumurta

Yəni, bunlar anbarda olan, amma satılmayan məhsullardır.

Vizualizasiya:

Anbar Satılıb Except (yalnız anbar)
Süd Süd
Yumurta Yumurta
Çörək Çörək (buraxılıb)
Pendir Pendir (buraxılıb)

Analoji

Bu, sanki sən öz sənədlər yığınından artıq göndərilənləri çıxarırsan — yalnız səndə qalanlar qalır.

Obyektlərlə nümunə

var unsold = stock.Except(sold);
foreach (var p in unsold)
    Console.WriteLine($"{p.Name} ({p.Category})");
// Çıxacaq: Süd (Süd məhsulları)

! Gözlənilməz məqamlar

Except metodu sıraya həssasdır: A.Except(B) — bu, B.Except(A) ilə eyni deyil! Birinci kolleksiya — "haradan çıxırıq", ikinci — "nəyi silirik".

5. LINQ ilə çoxlu əməliyyatların birləşdirilməsi və kompozisiyası

Bəzən bir əməliyyat kifayət etmir. Tutaq ki, yalnız anbarda və ya yalnız satılanlarda olan məhsulları çıxarmaq lazımdır — amma hər ikisində eyni anda olanları yox (bu, "simmetrik fərq" adlanır).

Simmetrik fərq ("XOR" çoxluqlar üçün):

var onlyInOne = warehouseProducts.Except(recentlySold)
                   .Union(recentlySold.Except(warehouseProducts));
foreach (var product in onlyInOne)
{
    Console.WriteLine(product);
}
// Çıxacaq: Süd, Yumurta, Salami, Çay

Mürəkkəb məntiq üçün LINQ metodlarını birləşdirmək rahatdır:

// "Gözlənilir" siyahısında olan, amma nə anbarda, nə də satılanlarda olmayan məhsulları tap
List<string> expected = new() { "Kofe", "Çay", "Süd" };
var onlyExpected = expected.Except(warehouseProducts.Union(recentlySold));
foreach (var product in onlyExpected)
    Console.WriteLine(product);
// Çıxacaq: Kofe

6. Custom tiplərlə iş və IEqualityComparer

Bəzən obyektləri bütün sahələrə görə yox, məsələn, yalnız məhsulun adına görə müqayisə etmək lazımdır, kateqoriya artıq vacib deyil. Bunun üçün LINQ metodları əlavə parametr — IEqualityComparer<T> dəstəkləyir, hansı ki, elementləri necə müqayisə etməyi müəyyən edir.

Öz comparator nümunəsi:

class ProductNameComparer : IEqualityComparer<Product>
{
    public bool Equals(Product? x, Product? y) => x?.Name == y?.Name;
    public int GetHashCode(Product obj) => obj.Name.GetHashCode();
}

var comp = new ProductNameComparer();
var uniqueByName = stock.Union(sold, comp);
foreach (var p in uniqueByName)
    Console.WriteLine(p.Name); // Süd, Çörək, Salami

Bu xüsusilə rahatdır, əgər sən Equals/GetHashCode-u bütün modeldə dəyişmək istəmirsənsə, sadəcə bir dəfə spesifik qayda ilə müqayisə etmək istəyirsənsə.

7. Vizual sxemlər və cədvəllər

Gəlin tətbiqləri ümumiləşdirək (siyahılar üçün A və B):

Əməliyyat Nəticə
A.Union(B)
A və ya B-də olan hər şey (unikal elementlər).
A.Intersect(B)
Hər ikisində (A və B) olan hər şey.
A.Except(B)
Yalnız A-da olan, amma B-də olmayan hər şey.

Sxem (Venn Diagram, çəkə bilsəydik):


      [A]        [B]
      oooooooooo
      oooooooo oooo
      ooooo oo   ooo
      ooo    ooo  oo
      oo      o  ooo
      ooo    ooo ooo
  • Union: hər iki dairənin içində olan hər şey.
  • Intersect: yalnız kəsişmə (mərkəz).
  • Except: yalnız A dairəsində olan, B ilə kəsişməyə toxunmayan.

8. Union, Intersect və Except istifadə edərkən tipik səhvlər

Səhv №1: obyektlərlə Equals və GetHashCode olmadan istifadə.
Əgər class-ında bu metodlar override olunmayıbsa, Union, Intersect və Except metodları düzgün işləməyəcək: eyni məzmuna malik obyektlər fərqli kimi qəbul olunacaq. Nəticədə gözlənilməz (və çox vaxt faydasız) nəticə alacaqsan.

Səhv №2: obyektləri sahələrin bir hissəsinə görə müqayisə etməyə cəhd, amma IEqualityComparer olmadan.
Məsələn, məhsulları yalnız adına görə müqayisə etmək istəyirsənsə, Intersect bunu başa düşməyəcək. Açıq IEqualityComparer olmadan nəticə gözlədiyin kimi olmayacaq.

Səhv №3: elementlərin sırası barədə yanlış gözləntilər.
Çoxları düşünür ki, nəticə kolleksiyası birləşmə və ya kəsişmənin sırasını saxlayır. Amma davranış metoda görə dəyişir: Union birinci kolleksiyanın sırasını saxlayır, amma Intersect və Except elementləri gözlənilməz sırada qaytara bilər. Ümumiyyətlə, sıraya güvənmə.

Səhv №4: böyük kolleksiyalarla işləyərkən performansı nəzərə almamaq.
Əgər məlumatlar böyükdürsə, metodlar yavaş işləyə bilər. Əvvəlcədən aqreqasiya, filtrasiya və ya hash-structurlar (məsələn, HashSet) istifadə etməyi düşün, əməliyyatları sürətləndirmək üçün.

2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
Ortaq elementləri tapmaq üçün Intersect istifadə edilməsi
Ortaq elementləri tapmaq üçün Intersect istifadə edilməsi
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION