Soru LINQ ile araya birleştirildi mi?


Şu anda LINQ ile biraz deneme yapıyorum. Diyelim ki aynı uzunlukta iki koleksiyonum var:

var first = new string[] { "1", "2", "3" };
var second = new string[] { "a", "b", "c" };

Bu iki koleksiyonu bir araya getirmek istiyorum ama bir araya getirilmiş bir şekilde. Sonuçta oluşan dizi şu şekilde olmalıdır:

"1", "a", "2", "b", "3", "c"

Şimdiye kadar ortaya çıkan şey, Zip, anonim tip ve SelectMany:

var result = first.Zip( second, ( f, s ) => new { F = f, S = s } )
                  .SelectMany( fs => new string[] { fs.F, fs.S } );

LINQ ile böyle bir araya getirilmiş birleştirme elde etmek için alternatif / basit bir yol bilen var mı?


26
2017-08-28 22:49


Menşei




Cevaplar:


Uyarı: Eğer numaralandırmaların farklı uzunlukları varsa, bu, takip eden öğeleri atlayacaktır. Daha kısa koleksiyonu doldurmak için boş değerlerin yerine geçmeyi tercih ederseniz, Andrew Shepherd'ın cevabı altında.


Kendi yazabilirsin Interleave gibi uzatma yöntemi bu örnek.

internal static IEnumerable<T> InterleaveEnumerationsOfEqualLength<T>(
    this IEnumerable<T> first, 
    IEnumerable<T> second)
{
    using (IEnumerator<T>
        enumerator1 = first.GetEnumerator(),
        enumerator2 = second.GetEnumerator())
    {
        while (enumerator1.MoveNext() && enumerator2.MoveNext())
        {
            yield return enumerator1.Current;
            yield return enumerator2.Current;
        }
    }
}

24
2017-08-28 22:53



Yeniden kullanılabilirlik ve okunabilirlik açısından kesinlikle iyi bir çözüm. - TeaWolf
İlk koleksiyon büyükse, bu kod yine de hepsini geri getirecektir, ancak eğer ikinci koleksiyon daha büyükse, onları atlardı. Belki tutarlılık için while döngüden sonra ikinci koleksiyonda devam etmeye değer :-) - Danny Tuppeny
@Danny, evet. Tercihim en kısa zamanda bittiğinde durmak olacaktır, o zaman boşlukları nasıl dolduracağınız konusunda endişelenmenize gerek yok. (Normalde diğer insanların kodlarını cevaplarıma koyamazdım, ama ChaosPandion'un düzenlemesini Jiri'nin koduyla birlikte yalnız bırakacağım.) - Douglas
@Douglas - Kodu ekledim, böylece orijinal kaynağın kaldırılması konusunda endişelenmemize gerek yok. Ayrıca tüm cevabınız, başlamak için kodlarına dayanır. - ChaosPandion
@Chaos, kabul etti. Eminim kod pasajı olmadan o kadar çok oy almazdı ki ... - Douglas


Sağladığınız örnek, anonim bir türle dağıtım yaparak daha basit hale getirilebilir:

   var result = first.Zip(second, (f, s) => new[] { f, s })
                      .SelectMany(f => f);

25
2017-08-28 23:05



Sadeleştirme için teşekkürler, işlerimi bir kez daha zorlaştırıyormuş gibi görünüyor. - TeaWolf
Bundan daha basit göremiyorum :) - MBen
eğer ilk dizinin 2 elemanı varsa ve ikinci dizinin 1 elemanı varsa, bu 2 eleman üretecektir. Bu uygulamaya dikkat edin - 2 listenin gerçek bir birleşimi değildir. - Denis


Kabul edilen cevapta verilen uygulamada tutarsızlık var:
Elde edilen dizilim her zaman birinci dizinin tüm unsurlarını içerecektir (dış yüzünden while döngü), ancak ikinci sıra daha fazla eleman içeriyorsa, bu elemanlar eklenmeyecektir.

Bir Interleave Yöntem sonuçta oluşan dizinin içereceğini beklerim

  1. sadece 'çiftleri' (sonuç dizisinin uzunluğu: min(length_1, length_2) * 2)), yada bu
  2. uzun dizinin kalan elemanları daima eklenir (sonuç dizisinin uzunluğu: length_1 + length_2).

Aşağıdaki uygulama ikinci yaklaşımı takip eder.
Bekar tek | kısa devre değerlendirmesini önleyen ya da karşılaştırmada.

public static IEnumerable<T> Interleave<T> (
    this IEnumerable<T> first, IEnumerable<T> second)
{
  using (var enumerator1 = first.GetEnumerator())
  using (var enumerator2 = second.GetEnumerator())
  {
    bool firstHasMore;
    bool secondHasMore;

    while ((firstHasMore = enumerator1.MoveNext())
         | (secondHasMore = enumerator2.MoveNext()))
    {
      if (firstHasMore)
        yield return enumerator1.Current;

      if (secondHasMore)
        yield return enumerator2.Current;
    }
  }
}

6
2018-03-25 15:59





Dizine bağlı olarak diziyi sadece döngü yapabilir ve seçebilirsiniz:

var result =
  Enumerable.Range(0, first.Length * 2)
  .Select(i => (i % 2 == 0 ? first : second)[i / 2]);

4
2017-08-28 23:11





var result = first.SelectMany( ( f, i ) => new List<string> { f, second[ i ] } );

1
2018-01-11 19:15