I'm currently experimenting a bit with LINQ. Let's say I have two collections of identical length:
var first = new string[] { "1", "2", "3" };
var second = new string[] { "a", "b", "c" };
I would like to merge those two collections into one, but in an interleaved fashion. The resulting sequence should thus be:
"1", "a", "2", "b", "3", "c"
What I've come up with so far is a combination of Zip
, an anonymous type and SelectMany
:
var result = first.Zip( second, ( f, s ) => new { F = f, S = s } )
.SelectMany( fs => new string[] { fs.F, fs.S } );
Does anybody know of an alternate/simpler way to achieve such an interleaved merge with LINQ?
The example you provided can by made simpler by dispensing with the anonymous type:
var result = first.Zip(second, (f, s) => new[] { f, s }) .SelectMany(f => f);
Warning: this will skip trailing elements if the enumerations have different lengths. If you'd rather substitute in nulls to pad out the shorter collection, use Andrew Shepherd's answer below.
You could write your own Interleave
extension method, like in this example.
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; } } }
The given implementation in the accepted answer has an inconsistency:
The resulting sequence will always contain all elements of the first sequence (because of the outer while
loop), but if the second sequence contains more elements, than those elements will not be appended.
From an Interleave
method I would expect that the resulting sequence contains
min(length_1, length_2) * 2)
), or thatlength_1 + length_2
).The following implementation follows the second approach.
Note the single |
in the or-comparison which avoids short-circuit evaluation.
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;
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With