CodeGym /Kurslar /C# SELF /Bir neçə mənbənin birləşdirilməsi:

Bir neçə mənbənin birləşdirilməsi: SelectMany LINQ-də

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

1. Giriş

Gəlin yadımıza salaq, adi Select necə işləyir:

var names = users.Select(user => user.Name);

Hər istifadəçiyə onun adı uyğun gəlir — adların siyahısını alırıq. Hər şey sadədir! Bəs biz bütün istifadəçilərin bütün sifarişlərinin siyahısını almaq istəsək?

var ordersList = users.Select(user => user.Orders);

Burada nə baş verəcək? Hər istifadəçi üçün sifarişlərin siyahısı alacağıq. Yəni nəticə kolleksiyaların kolleksiyası olacaq (IEnumerable<List<Order>>). Bu, sanki rəfdə qutular var: hər sifarişi tapmaq üçün əllə qutunu (istifadəçinin sifariş siyahısı) bir-bir açmalısan.

Biz istəyirik ki, bütün bu massiv "açılsın" və onunla adi sifariş siyahısı kimi işləyək. Bax, burada SelectMany köməyə gəlir.

Bənzətmə: qutuların açılması

Təsəvvür elə ki, bütün əşyaların qarışıq yox, qutulardadır. Hər qutu — bir kolleksiyadır, məsələn, bir istifadəçinin alışları. Əgər sən BÜTÜN əşyaları birdən görmək istəyirsənsə (məsələn, ümumi yoxlama üçün), əvvəlcə bütün qutuları, sonra içindəkiləri bir-bir açmazsan. Ən rahatı — bütün qutuların içindəkiləri bir yerə tökməkdir və artıq o yığından istifadə etməkdir. SelectMany də məhz belə işləyir.

2. SelectMany nədir?

SelectMany — bu LINQ metodudur, kolleksiyaların kolleksiyasını bir "düz" kolleksiyaya çevirir. "Düz" dedikdə, nəticədə artıq iç-içə siyahılar olmayacaq — bütün elementlər bir səviyyədə olacaq, sanki onları "çölə çıxarıblar". Heç bir matryoşka yoxdur, sadəcə uzun bir dəyər zənciri.

Metodun imzası belədir:


IEnumerable<TResult> SelectMany<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, IEnumerable<TResult>> selector
)

İş prinsipi sadədir: səndə bir kolleksiya var (məsələn, istifadəçilərin siyahısı), hər elementin içində başqa bir kolleksiya var (məsələn, həmin istifadəçinin sifarişləri). SelectMany hər istifadəçini götürür, onun sifarişlərini çıxarır və hamısını bir axına birləşdirir — "qablaşdırma" olmadan, iç-içə olmadan. Məhz bu, "siyahıların siyahısı"ndan adi siyahıya keçmək lazım olanda çox rahatdır.

3. Sadə nümunələr

Nümunə 1: İstifadəçilər və onların sifarişləri

Tətbiqimizin modelindən başlayaq, hansı ki, biz onu tədricən inkişaf etdiririk:

// Tutaq ki, bu class-lar əvvəlki dərslərdən var
class User
{
    public string Name { get; set; }
    public List<Order> Orders { get; set; }
}

class Order
{
    public int Id { get; set; }
    public string ProductName { get; set; }
}

Tutaq ki, istifadəçilərin siyahısı var:

var users = new List<User>
{
    new User
    {
        Name = "İvan",
        Orders = new List<Order>
        {
            new Order { Id = 1, ProductName = "Kitab" },
            new Order { Id = 2, ProductName = "Qələm" }
        }
    },
    new User
    {
        Name = "Mariya",
        Orders = new List<Order>
        {
            new Order { Id = 3, ProductName = "Noutbuk" }
        }
    }
};

Tapşırıq: bütün istifadəçilər tərəfindən nə vaxtsa alınmış bütün məhsulların siyahısını almaq.

Adi Select:

var ordersPerUser = users.Select(user => user.Orders);
// IEnumerable<List<Order>>

Burada çıxışda bir çox "qutu" var, hər birində istifadəçiyə görə sifarişlərin siyahısı.

SelectMany:

var allOrders = users.SelectMany(user => user.Orders);
// IEnumerable<Order>

İndi biz bütün sifarişlərin bir "uzun" kolleksiyasını alırıq!

Vizualizasiya

İstifadəçi Sifarişlər
İvan Kitab, Qələm
Mariya Noutbuk
  • Select-dən sonra: [[Kitab, Qələm], [Noutbuk]] — siyahıların siyahısı
  • SelectMany-dən sonra: [Kitab, Qələm, Noutbuk] — birləşdirilmiş siyahı

Nümunə 2: Bir neçə səviyyəli iç-içəliklə işləmək

Əgər məsələn, sifarişin içində məhsulların siyahısı varsa (səbət), onda SelectMany bir neçə dəfə tətbiq oluna bilər — matryoşka kimi!

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

class Order
{
    public int Id { get; set; }
    public List<Product> Products { get; set; }
}

class User
{
    public string Name { get; set; }
    public List<Order> Orders { get; set; }
}

var users = new List<User>
{
    new User
    {
        Name = "Petr",
        Orders = new List<Order>
        {
            new Order
            {
                Id = 10,
                Products = new List<Product>
                {
                    new Product { Name = "Telefon" },
                    new Product { Name = "Zaryadka" }
                }
            }
        }
    }
};

Bütün istifadəçilərin bütün sifarişlərinin bütün məhsullarını almaq üçün ikiqat SelectMany istifadə edirik:

var allProducts = users
    .SelectMany(u => u.Orders)
    .SelectMany(o => o.Products);
// IEnumerable<Product>

Bu üsulla əvvəlcə bir səviyyə "qutuları", sonra növbəti səviyyəni açırsan.

Nümunə 3: SelectMany Query Syntax-da istifadə

Əgər sənə SQL-ə bənzər LINQ sintaksisi (Query Syntax) daha rahatdırsa, SelectMany bir neçə from ardıcıl yazmaqla ifadə olunur:

var allProducts =
    from user in users
    from order in user.Orders
    from product in order.Products
    select product;

Burada hər növbəti from əvvəlki tərəfindən qaytarılan kolleksiyadan element götürür.


users
  └─ user1
        └─ order1 
             └─ product1, product2
        └─ order2
             └─ product3
  └─ user2
        └─ order3
             └─ product4
SelectMany üçün iç-içə kolleksiyaların sxemi

Query Syntax bütün yolları keçir və bir uzun məhsul siyahısı qaytarır.

4. İstifadə variantları və incəliklər

SelectMany ilə çevirmə və filtrasiya

SelectMany ilə təkcə sadə açma yox, həm də eyni anda filtrasiya və ya proyeksiya etmək olar. Məsələn, bütün sifarişlərdən yalnız 1000 avrodan baha məhsulları seçmək:

var expensiveProducts = users
    .SelectMany(u => u.Orders)
    .SelectMany(o => o.Products)
    .Where(p => p.Price > 1000);

Və ya — bir az əlavə məntiqi birbaşa SelectMany-in içinə yerləşdirmək (əlavə selector ilə):

var expensiveProducts = users
    .SelectMany(u => u.Orders.SelectMany(o => o.Products))
    .Where(p => p.Price > 1000);

Bu, zövq məsələsidir: hər iki variant problemi ardıcıl addımlara bölür.

Proyeksiya ilə SelectMany overload istifadə etmək

Əlavə "pro" overload metodu təkcə açmaq yox, həm də nəticəni dərhal formalaşdırmağa imkan verir. Məsələn: "İstifadəçi adı — Məhsul adı" cütlərini almaq:

var userProductPairs = users.SelectMany(
    user => user.Orders.SelectMany(order => order.Products),
    (user, product) => new { user.Name, product.Name }
);

Bu necə işləyir? Birinci parametr — user => user.Orders.SelectMany(order => order.Products) — istifadəçinin bütün məhsullarını açır, ikinci isə istifadəçi obyektini və məhsulu iç-içə enumerator-da birləşdirir.

5. Vacib məqamlar və tez-tez edilən səhvlər

Belə vəziyyət ola bilər ki, SelectMany əlavə etməyi unutmusan və düz ardıcıllıq əvəzinə "qutular" massivi almısan. Məsələn, adi Select istifadə etmisən, halbuki bütün elementləri açmaq lazım idi. Nəticədə elementləri birbaşa işləmək olmur.

Çox asanlıqla çaşmaq olar, əgər kolleksiyalar iç-içədirsə, xüsusilə də iç-içəlik çoxdursa. Qayda kimi götür: əgər çıxışda siyahıların siyahısı yox, düz siyahı lazımdırsa, — rahatlıqla SelectMany istifadə et.

Harada praktikada lazım olacaq?

Hər hansı tapşırıqda, əgər səndə obyekt-kolleksiya var və hər elementin öz kolleksiyası varsa (məsələn, "istifadəçi — sifarişlər", "kurs — tələbələr", "xəbər — şərhlər"), sən SelectMany-ə qayıdacaqsan. Müsahibələrdə bu "sevimli" mövzulardan biridir: yeni başlayanlar SelectSelectMany fərqini həmişə başa düşmür və kolleksiyaları düzgün aça bilmirlər.

Real layihələrdə bu, kodu gözəl, qısa və sürətli yazmağa imkan verir, əlavə iç-içəlik olmadan. .NET platformasında bu, "qat-qat" kolleksiyaları işləmək üçün standart və optimallaşdırılmış yoldur.

SelectSelectMany arasındakı fərq

Select metodu strukturu saxlayır: əgər sən onu istifadəçilər kolleksiyasına tətbiq edirsənsə və hər birinin sifariş siyahısı varsa, nəticə kolleksiyaların kolleksiyası olacaq — məsələn, [[A, B], [C]]. Bu, əgər sən məlumatlarla qruplar üzrə işləmək istəyirsənsə (məsələn, istifadəçilərə görə), rahatdır.

Amma SelectMany "yastılaşdırma" edir — bütün iç-içə kolleksiyaları bir ümumi ardıcıllığa birləşdirir: [A, B, C]. Bu, qruplaşdırma vacib deyilsə və bütün daxili elementlərlə bir yerdə işləmək istəyirsənsə faydalıdır — məsələn, bütün məhsulları tapmaq, fərq etməz hansı istifadəçi və ya sifarişə aiddir.


+---------------------+         +--------------------+
| users               |         | allOrders          |
+---------------------+         +--------------------+
| İvan: [A, B]        | --+   +-> Order A           |
| Mariya: [C]         | --+ | +-> Order B           |
+---------------------+   | | +-> Order C           |
                          | | +--------------------+
     Select:              | |
   [[A, B], [C]] <--------+ +
                          |
     SelectMany: <--------+
   [A, B, C]
SelectSelectMany arasındakı fərqin sxemi

Qaba şəkildə desək:

  • Select → "mənə istifadəçilərə görə sifariş siyahılarını ver";
  • SelectMany → "mənə sadəcə bütün sifarişləri ver".
2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
SelectMany metodundan istifadə edərək kolleksiyaları birləşdirmək
SelectMany metodundan istifadə edərək kolleksiyaları birləşdirmək
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION