CodeGym /Kurslar /C# SELF /Pozisiyaya görə birləşdirmə

Pozisiyaya görə birləşdirmə Zip ilə

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

1. Giriş

Təsəvvür elə iki məktub dəstəsi var: birincisi — üstündə adlar yazılmış zərflər, ikincisi — həmin ünvanlara açıqcalar. Sənin işin — hər zərfi ikinci dəstədə eyni mövqedə olan açıqca ilə uyğunlaşdırmaqdır. Birinci zərf — birinci açıqca ilə, ikinci — ikinci ilə və s. Hər iki dəstədən bir-bir element götürürsən və onları "zip" edirsən.

Proqramlaşdırmada bu əməliyyata zip deyirlər — fermuar kimi, iki tərəfi bir-birinə mövqeyə görə "bağlayır".

LINQ-də bu, iki (bəzən daha çox) ardıcıllıqdan məlumatları birləşdirmək üçün rahat və güclü alətdir, sıra vacib olanda: birinci element birinci ilə, ikinci — ikinci ilə və s.
Vacibdir: Zip yalnız hər iki kolleksiyada element olduqca işləyir. Əgər biri daha qısadırsa — nəticə ən qısa kolleksiyaya qədər kəsiləcək.

Sintaksis və işləmə prinsipləri

Zip iki (və ya daha çox) siyahını götürür və onları yenisinə birləşdirir: elementlər indeksə görə birləşir, yəni 0-cı 0-cı ilə, 1-ci 1-ci ilə və s. Əgər siyahılardan biri tez bitərsə, yeni cütlər artıq "zip" olunmayacaq — nəticə ən qısa siyahının uzunluğunda olacaq.

Əsas imza:


IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
    this IEnumerable<TFirst> first,
    IEnumerable<TSecond> second,
    Func<TFirst, TSecond, TResult> resultSelector
)

Diqqət elə: Zip sadəcə cütlər qaytarmır, sən resultSelector içində nə göstərirsənsə onu qaytarır. Bu tuple, string, obyekt və ya istədiyin hər şey ola bilər — sənin məntiqindən asılıdır.

  • first və second — birləşdiriləcək kolleksiyalar.
  • resultSelector — hər iki kolleksiyadan növbəti elementləri qəbul edib yeni nəticə qaytaran funksiya.

Sadə dillə: hər iki kolleksiyadan eyni anda gedirik, hər addımda onların cütündən bizə lazım olanı düzəldirik.

2. Zip istifadəsinə nümunələr — sadədən mürəkkəbə

Ən sadə nümunə: iki ədədi massiv toplamaq

Tutaq ki, iki siyahımız var:

  • Riyaziyyat balları
  • Ədəbiyyat balları
Hər tələbə üçün eyni mövqedə ümumi bal tapmaq lazımdır.


// Tələbələrin balları üçün iki massiv
int[] mathScores = { 5, 4, 3, 5, 2 };
int[] literatureScores = { 4, 5, 3, 4, 3 };

// Mövqeyə görə birləşdirib toplayırıq
var totalScores = mathScores.Zip(literatureScores, (math, literature) => math + literature);

foreach (var score in totalScores)
    Console.WriteLine($"Tələbənin ümumi balı: {score}");

Tələbənin ümumi balı: 9
Tələbənin ümumi balı: 9
Tələbənin ümumi balı: 6
Tələbənin ümumi balı: 9
Tələbənin ümumi balı: 5

Gördüyün kimi, hər şey çox məntiqlidir: birinci tələbə birinci balların cəmini alır, ikinci — ikincilərin və s.

Sətirləri və ədədləri birləşdiririk: məhsullara qiymət yazırıq

Tutaq ki, məhsullar siyahın və qiymətlər siyahın var. Çıxışda belə olmalıdır: "ProductName — Price".


string[] productNames = { "Alma", "Armud", "Banan" };
decimal[] productPrices = { 50.5m, 60.0m, 35.2m };

var info = productNames.Zip(productPrices, (name, price) => $"{name} — {price} avro");

foreach (var s in info)
    Console.WriteLine(s);

Alma — 50,5 avro
Armud — 60,0 avro
Banan — 35,2 avro

Obyekt kolleksiyaları ilə istifadə

Tutaq ki, əvvəlki nümunələrdə Student klası və tələbələr kolleksiyası var idi. İndi isə — sıraya uyğun ayrıca reytinqlər massivi:


public class Student
{
    public string Name { get; set; }
}

List<Student> students = new List<Student>
{
    new Student { Name = "Vasya" },
    new Student { Name = "Petya" },
    new Student { Name = "Maşa" }
};

int[] ratings = { 7, 9, 8 };

var studentsWithRatings = students.Zip(ratings, (student, rating) =>
    $"{student.Name}: reytinq {rating}");

foreach (var s in studentsWithRatings)
    Console.WriteLine(s);

Vasya: reytinq 7
Petya: reytinq 9
Maşa: reytinq 8

Daha mürəkkəb obyektləri birləşdiririk

Həyatda tez-tez, məsələn, iki fərqli sistemdən sifariş siyahılarını birləşdirmək lazım olur: hər sifarişə işlənmə nəticəsi və ya status uyğunlaşdırmaq.


string[] orders = { "A-1", "B-2", "C-3" };
string[] statuses = { "Yerinə yetirildi", "Prosesdə", "Rədd edildi" };

var orderStatusList = orders.Zip(statuses, (order, status) => new { Order = order, Status = status });

foreach (var os in orderStatusList)
    Console.WriteLine($"Sifariş {os.Order}: {os.Status}");

Sifariş A-1: Yerinə yetirildi
Sifariş B-2: Prosesdə
Sifariş C-3: Rədd edildi

4. Vacib xüsusiyyətlər və tipik səhvlər

Zip istifadə edəndə unutma: nəticə kolleksiyası ən qısa daxil olan kolleksiya qədər olacaq.
Məsələn, 10 tələbən var, amma cəmi 7 qiymət — nəticədə yalnız 7 element olacaq. Bu, tez-tez gözlənilməz "itkilərin" səbəbidir.

Səhv №1: kolleksiyaların uzunluğu və ya sırası uyğun deyil.
Əgər siyahıların sırasını səhv saldınsa və ya əvvəlcədən onları uyğun sıralamadınsa, nəticə cütlər səhv olacaq.
Təsəvvür elə: tələbə siyahısı qiymətlərin sırası ilə uyğun gəlmir — Vasya Petya-nın qiymətini ala bilər. Bu, fərqli corab geyinmək kimidir: hərəsi yerindədir, amma hiss qəribədir.

Səhv №2: kolleksiyalardan biri boşdur.
Əgər siyahılardan biri boşdursa, nəticə də boş olacaq.
Zip yalnız hər iki ardıcıllıqda element olduqda işləyir. Əgər mənbələrdən biri boşdursa, "heç nə" alacaqsan.

5. Bir neçə kolleksiya ilə Zip

Əvvəllər LINQ yalnız iki kolleksiyanı birləşdirməyə imkan verirdi. Amma .NET 6-dan başlayaraq, artıq ÜÇ (vau!) kolleksiyanı eyni anda zip etmək olur.

Üç kolleksiya ilə nümunə:


string[] names = { "Rey", "Lük", "Leya" };
string[] planets = { "Tatuin", "Tatuin", "Alderaan" };
int[] ages = { 19, 23, 19 };

// .NET 6+ üçün overload:
var characters = names.Zip(planets, ages, (name, planet, age) =>
    $"{name} ({planet}), {age} yaş");

foreach (var c in characters)
    Console.WriteLine(c);

Rey (Tatuin), 19 yaş
Lük (Tatuin), 23 yaş
Leya (Alderaan), 19 yaş

Nəticədə elementlərin sayı — ən qısa siyahı qədər! Əgər ages bir dənə az olsa, sonuncu Lukas "yaşsız" qalacaq.

6. Zip və LINQ Query Syntax: mövcuddurmu?

Yəqin fikir vermisən ki, biz həmişə "metod" sintaksisindən istifadə edirik (Method Syntax). LINQ Query Syntax-da Zip üçün ayrıca söz yoxdur və bunu from ... in ... ilə etmək olmur. Səbəb sadədir: "zip" konsepsiyası — mövqeyə görə cütləşdirmədir, LINQ query-ləri isə əsasən join, select və s. ilə "açar" üzrə birləşdirmə üçündür, mövqeyə görə yox.

Nəticə: Zip üçün yalnız nöqtəli (yəni metod) sintaksis istifadə edirik:


var zipped = collection1.Zip(collection2, (a, b) => ...);

Əgər query-sintaksis istifadə etmək istəyirsənsə — yaxşı deyil. Amma çox-çox istəyirsənsə, öz extension metodunu yaza bilərsən. 🙂

7. Zip real tətbiqlərdə

Sənin praktikan üçün nümunə: köhnə və yeni qiymətləri müqayisə

Tutaq ki, bizim tədris tətbiqində məhsullar siyahısı var və onlara vaxtaşırı yeni qiymətlər gəlir. Hər məhsul üçün qiymətin necə dəyişdiyini göstərmək lazımdır:


string[] productNames = { "Alma", "Banan", "Ananas" };
decimal[] oldPrices = { 80.0m, 30.0m, 110.0m };
decimal[] newPrices = { 75.0m, 33.0m, 120.0m };

var priceChanges = productNames
    .Zip(oldPrices, newPrices, (name, oldPrice, newPrice) => 
        $"{name}: əvvəl {oldPrice}, indi {newPrice}, dəyişiklik: {newPrice - oldPrice:+#;-#;0}");

foreach (var s in priceChanges)
    Console.WriteLine(s);

Alma: əvvəl 80, indi 75, dəyişiklik: -5
Banan: əvvəl 30, indi 33, dəyişiklik: +3
Ananas: əvvəl 110, indi 120, dəyişiklik: +10

Praktika: fərqli mənbələrdən nəticələri birləşdirmək

"Real" layihələrdə Zip tez-tez müxtəlif API və ya cədvəllərdən nəticələri birləşdirmək üçün istifadə olunur, harada ki, məlumatların sırası xaricdən təmin olunur (məsələn, hava proqnozu və faktiki ölçmələr massivləri).

8. LINQ zəncirlərində Zip: digər operatorlarla birləşdiririk

Çox vaxt Zip təkbaşına yox, uzun LINQ zəncirlərində istifadə olunur. Məsələn, əvvəlcə siyahıları filterləyirsən, sonra zip edirsən, sonra isə yekun statistika hesablayırsan.


var passedMath = mathScores.Where(x => x >= 3);
var passedLit = literatureScores.Where(x => x >= 3);

var passedPairs = passedMath.Zip(passedLit, (m, l) => m + l);
var avgScore = passedPairs.Average();

Console.WriteLine($"İmtahanı keçənlərin orta ümumi balı: {avgScore}");

Fint: Where sonrası siyahılardan biri qısaldısa, Zip nəticəni ən qısa siyahıya görə kəsəcək.

Zip və digər birləşdirmə metodlarının müqayisəsi

Metod Məğzi Nəyə görə birləşdirir… Nəticənin uzunluğu…
Zip
Elementləri indeksə görə birləşdirir İndeks (mövqe) Qısa kolleksiya
Join
Açar üzrə birləşdirir Ümumi açar Bütün uyğun cütlər
GroupJoin
Hər açar qrupu üçün kolleksiya Açar Birinci kolleksiya üzrə
Concat
Kolleksiyaları ard-arda yapışdırır Sadəcə sona Uzunluqların cəmi

Zip — bu, mövqeyə görə uyğunluq üçündür. Join — ümumi dəyərə (məsələn, kod və ya id) görə uyğunluq üçündür.

9. Praktik məsləhətlər və tipik səhvlər

Zip tətbiqində əsas odur ki:

  • Hər iki kolleksiyada elementlərin sırası uyğun olsun: yoxsa "Vasya" ilə "Petya"nın qiymətini birləşdirərsən.
  • Kolleksiyalar düzgün sıralanıb və ya sinxron iterasiya üçün əvvəlcədən hazırlanıb.
  • Nəticə həmişə ən qısa kolleksiya qədər olacaq.
  • Əgər məlumatları açara görə uyğunlaşdırmaq lazımdırsa, Zip istifadə etmə! Bunun üçün Join var.
  • Daha bir tipik səhv — bir kolleksiyada dublikatlar var, digərində yox. Və ya filterləmə zamanı elementlərin sırasını dəyişdin və uyğunluq itdi.
2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
Qiymətləri ilə məhsullar siyahısı
Qiymətləri ilə məhsullar siyahısı
1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
Kolleksiyaların birləşdirilməsi
İrəli səviyyə LINQ: join-lar və proyeksiyalar
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION