Vraag Interleaved samenvoegen met LINQ?


Ik experimenteer momenteel een beetje met LINQ. Laten we zeggen dat ik twee collecties van dezelfde lengte heb:

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

Ik zou graag die twee collecties samenvoegen tot één, maar op een interleaved manier. De resulterende reeks zou dus moeten zijn:

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

Wat ik tot nu toe heb bedacht is een combinatie van Zip, een anoniem type en SelectMany:

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

Weet iemand van een alternatieve / eenvoudigere manier om zo'n verweven fusie met LINQ te bereiken?


26
2017-08-28 22:49


oorsprong


antwoorden:


Waarschuwing: dit zal slepende elementen overslaan als de opsommingen verschillende lengtes hebben. Als u liever in null's wilt substitueren om de kortere verzameling op te slaan, gebruikt u Het antwoord van Andrew Shepherd hieronder.


Je zou je eigen kunnen schrijven Interleave uitbreidingsmethode, zoals in dit voorbeeld.

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



Het voorbeeld dat u hebt opgegeven, kan eenvoudiger worden door het anonieme type af te schaffen:

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

25
2017-08-28 23:05



De gegeven implementatie in het geaccepteerde antwoord heeft een inconsistentie:
De resulterende reeks bevat altijd alle elementen van de eerste reeks (vanwege de buitenste reeks while loop), maar als de tweede reeks meer elementen bevat, dan worden die elementen niet toegevoegd.

Van een Interleave methode die ik zou verwachten dat de resulterende sequentie bevat

  1. alleen 'paren' (lengte van de resulterende reeks: min(length_1, length_2) * 2)), of dat
  2. de resterende elementen van de langere reeks worden altijd toegevoegd (lengte van de resulterende reeks: length_1 + length_2).

De volgende implementatie volgt de tweede benadering.
Let op de single | in de of-vergelijking die kortsluitingsevaluatie vermijdt.

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



U kunt de array gewoon doorlopen en selecteren, afhankelijk van de index:

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