Birden çok öğe koleksiyonunu birleştirmenin zarif bir yolu mu?


98

Her biri aynı türde nesneler içeren (örneğin, List<int> foove List<int> bar) rastgele sayıda koleksiyonum olduğunu varsayalım . Bu koleksiyonların kendileri bir koleksiyonda olsaydı (örneğin, türden List<List<int>>, SelectManyhepsini tek bir koleksiyonda birleştirmek için kullanabilirdim .

Ancak bu koleksiyonlar halihazırda aynı koleksiyonda değilse, benim izlenimime göre şöyle bir yöntem yazmalıyım:

public static IEnumerable<T> Combine<T>(params ICollection<T>[] toCombine)
{
   return toCombine.SelectMany(x => x);
}

Daha sonra şöyle derim:

var combined = Combine(foo, bar);

CombineYukarıdaki gibi bir yardımcı yöntem yazmak zorunda kalmadan koleksiyonları (herhangi bir sayıda) birleştirmenin temiz ve zarif bir yolu var mı ? LINQ'da bunu yapmanın bir yolu olması gerektiği kadar basit görünüyor, ama belki de değil.



Çok sayıda numaralandırmayı birleştirmek için
Concat'a

Yanıtlar:


112

Sanırım LINQ'lar arıyor olabilirsiniz .Concat()?

var combined = foo.Concat(bar).Concat(foobar).Concat(...);

Alternatif olarak, .Union()yinelenen öğeleri kaldıracaktır.


2
Teşekkürler; İlk başta bunu yapmamamın tek nedeni, daha fazla koleksiyonla uğraşmak zorunda kaldıkça (bana göre) çirkinleşiyor olması. Ancak bu, gelecekteki geliştiricilerin zaten aşina olacağı mevcut LINQ işlevlerini kullanma avantajına sahiptir.
Donut

2
Bahşiş için teşekkürler .Union(). Böyle bir durumda özel türünüzde IComparer uygulamanız gerekeceğini unutmayın .
Gonzo345

30

Bana göre Concatben concat için birden çok büyük dizileri varken bir uzantısı yöntemi benim kodunda çok şık değil gibi. Bu yalnızca bir kod girintisi / biçimlendirme sorunu ve çok kişisel bir şey.

Elbette şöyle güzel görünüyor:

var list = list1.Concat(list2).Concat(list3);

Şöyle okunduğunda çok okunamaz:

var list = list1.Select(x = > x)
   .Concat(list2.Where(x => true)
   .Concat(list3.OrderBy(x => x));

Veya göründüğü zaman:

return Normalize(list1, a, b)
    .Concat(Normalize(list2, b, c))
       .Concat(Normalize(list3, c, d));

veya tercih ettiğiniz biçimlendirme ne olursa olsun. Daha karmaşık sonuçlarla işler daha da kötüleşir. Benim nedeni tür yukarıdaki tarzı ile bilişsel uyumsuzluk ilk dizisi yalan dışında olmasıdır Concatmüteakip diziler ise yöntemi içinde yalan. ConcatUzantı stilini değil, doğrudan statik yöntemi çağırmayı tercih ederim :

var list = Enumerable.Concat(list1.Select(x => x),
                             list2.Where(x => true));

Daha fazla sayıda dizi bitişi için, OP'deki ile aynı statik yöntemi kullanıyorum:

public static IEnumerable<T> Concat<T>(params IEnumerable<T>[] sequences)
{
    return sequences.SelectMany(x => x);
}

Böylece yazabilirim:

return EnumerableEx.Concat
(
    list1.Select(x = > x),
    list2.Where(x => true),
    list3.OrderBy(x => x)
);

Daha iyi görünüyor. Yazmam gereken fazladan, aksi halde fazlalık olan sınıf adı, Concataramayla sıralarımın daha temiz göründüğünü düşünürsek benim için sorun değil . C # 6'da daha az sorun . Sadece yazabilirsiniz:

return Concat(list1.Select(x = > x),
              list2.Where(x => true),
              list3.OrderBy(x => x));

C # 'da birleştirme işleçlerini listelemeyi diledik, şöyle bir şey:

list1 @ list2 // F#
list1 ++ list2 // Scala

Böylesi çok daha temiz.


4
Kodlama stiline olan ilginizi takdir ediyorum. Bu çok daha zarif.
Bryan Rayner

Belki cevabınızın daha küçük bir versiyonu buradaki en iyi cevap ile birleştirilebilir.
Bryan Rayner

2
Bu aynı zamanda biçimlendirme gibi - I (örn bireysel liste işlemleri gerçekleştirmek için tercih rağmen Where, OrderBynetlik için ilk), özellikle bu örnekte daha karmaşık onlar konum eğer bir şey.
brichins

27

Eğer durum için yapmak yani koleksiyonları, bir koleksiyonu var List<List<T>>, Enumerable.Aggregatebirine tüm listeler birleştirmek için daha zarif bir yoludur:

var combined = lists.Aggregate((acc, list) => { return acc.Concat(list); });

1
listsBuraya ilk nasıl ulaşırsınız ? OP'nin sahip olduğu ilk sorun budur. Başlaması gereken bir şey varsa, o collection<collection>zaman SelectManyçok daha basittir.
nawfal

Ne olduğundan emin değilim, ama bu yanlış soruyu yanıtlıyor gibi görünüyor. Bunu yansıtmak için yanıt düzenlenmiştir.
Görünüşe göre



7

Aggregate'i Concat ile birlikte her zaman kullanabilirsiniz ...

        var listOfLists = new List<List<int>>
        {
            new List<int> {1, 2, 3, 4},
            new List<int> {5, 6, 7, 8},
            new List<int> {9, 10}
        };

        IEnumerable<int> combined = new List<int>();
        combined = listOfLists.Aggregate(combined, (current, list) => current.Concat(list)).ToList();

1
Şimdiye kadarki en iyi çözüm.
Oleg

@Oleg SelectManysadece daha basit.
nawfal

6

Gördüğüm tek yol kullanmak Concat()

 var foo = new List<int> { 1, 2, 3 };
 var bar = new List<int> { 4, 5, 6 };
 var tor = new List<int> { 7, 8, 9 };

 var result = foo.Concat(bar).Concat(tor);

Ama neyin daha iyi olduğuna karar vermelisiniz:

var result = Combine(foo, bar, tor);

veya

var result = foo.Concat(bar).Concat(tor);

Neden Concat()daha iyi bir seçim olacağı bir nokta , başka bir geliştirici için daha açık olacağıdır. Daha okunaklı ve basit.


3

Union'ı şu şekilde kullanabilirsiniz:

var combined=foo.Union(bar).Union(baz)...

Bu aynı öğeleri kaldıracaktır, bu yüzden bunlara sahipseniz Concat, bunun yerine kullanmak isteyebilirsiniz .


4
Hayır! Enumerable.Unionistenen sonucu vermeyen bir set birleşimidir (yalnızca bir kez kopya verir).
jason

Evet, nesnelerin belirli örneklerine ihtiyacım var çünkü üzerlerinde bazı özel işlemler yapmam gerekiyor. Örneğimde kullanmak intinsanları biraz şaşırtmış olabilir; ama Unionbu durumda benim için çalışmayacak.
Donut

2

Koleksiyon Başlatıcılarını kullanan birkaç teknik -

bu listeleri varsayarak:

var list1 = new List<int> { 1, 2, 3 };
var list2 = new List<int> { 4, 5, 6 };

SelectMany bir dizi başlatıcı ile (benim için pek zarif değil, ancak herhangi bir yardımcı işleve güvenmiyor):

var combined = new []{ list1, list2 }.SelectMany(x => x);

Veriyor Ekleme için bir liste uzantısı tanımlayın IEnumerable<T>içinde List<T> başlatıcısı :

public static class CollectionExtensions
{
    public static void Add<T>(this ICollection<T> collection, IEnumerable<T> items)
    {
        foreach (var item in items) collection.Add(item);
    }
}

Daha sonra bunun gibi diğerlerinin öğelerini içeren yeni bir liste oluşturabilirsiniz (aynı zamanda tek tek öğelerin karıştırılmasına da izin verir).

var combined = new List<int> { list1, list2, 7, 8 };

1

Bir sürü ayrı koleksiyonla başladığınıza göre, çözümünüzün oldukça zarif olduğunu düşünüyorum. Onları birbirine dikmek için bir şeyler yapmanız gerekecek .

Combine yönteminizden bir genişletme yöntemi oluşturmak sözdizimsel olarak daha uygun olacaktır, bu da onu gittiğiniz her yerde kullanılabilir hale getirir.


Uzatma yöntemi fikrini beğendim, LINQ zaten aynı şeyi yapacak bir yönteme sahipse (ve hattın ilerisindeki diğer geliştiriciler tarafından hemen anlaşılabilir hale geldiyse) tekerleği yeniden icat etmek istemedim
Donut

1

İhtiyacınız olan tek şey şudur IEnumerable<IEnumerable<T>> lists:

var combined = lists.Aggregate((l1, l2) => l1.Concat(l2));

Bu, tüm öğeleri listsbir IEnumerable<T>(kopyalarla) birleştirecektir. Diğer cevaplarda belirtildiği gibi, kopyaları kaldırmak için Unionyerine kullanın Concat.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.