This is a great opportunity to write your own extension method to merge IEnumerable
s
public static class EnumerableMergeExtension
{
public static IEnumerable<TSource> Merge<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second)
{
if (first is null) throw new ArgumentNullException(nameof(first));
if (second is null) throw new ArgumentNullException(nameof(second));
return MergeIterator(first, second);
}
private static IEnumerable<TSource> MergeIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second)
{
using var firstEnumerator = first.GetEnumerator();
using var secondEnumerator = second.GetEnumerator();
var firstHasNext = firstEnumerator.MoveNext();
var secondHasNext = secondEnumerator.MoveNext();
do
{
if (firstHasNext)
{
yield return firstEnumerator.Current;
firstHasNext = firstEnumerator.MoveNext();
}
if (secondHasNext)
{
yield return secondEnumerator.Current;
secondHasNext = secondEnumerator.MoveNext();
}
}
while (firstHasNext || secondHasNext);
}
}
By manually advancing the enumerators you can handle enumerables of different lengths, even empty enumerables.
The function is split in two to ensure eager parameter validation and lazy enumeration.
It's also possible to extend Merge
to handle any number of enumerables without having to chain Zip
public static class EnumerableMergeExtension
{
public static IEnumerable<TSource> Merge<TSource>(this IEnumerable<TSource> first, params IEnumerable<TSource>[] rest)
{
if (first is null) throw new ArgumentNullException(nameof(first));
if (rest is null) throw new ArgumentNullException(nameof(rest));
var enumerables = new IEnumerable<TSource>[rest.Length + 1];
enumerables[0] = first;
for(int i = 0; i < rest.Length; i++)
{
enumerables[i+1] = rest[i] ?? throw new ArgumentNullException(string.Format("{0}[{1}]", nameof(rest), i));
}
return MergeIterator(enumerables);
}
private static IEnumerable<TSource> MergeIterator<TSource>(IEnumerable<TSource>[] sources)
{
var enumerators = new IEnumerator<TSource>[sources.Length];
try
{
for(int i = 0; i < sources.Length; i++)
{
enumerators[i] = sources[i].GetEnumerator();
}
var hasNexts = new bool[enumerators.Length];
bool MoveNexts()
{
var anyHasNext = false;
for (int i = 0; i < enumerators.Length; i++)
{
anyHasNext |= hasNexts[i] = enumerators[i].MoveNext();
}
return anyHasNext;
}
while(MoveNexts())
{
for (int i = 0; i < enumerators.Length; i++)
{
if(hasNexts[i])
{
yield return enumerators[i].Current;
}
}
}
}
finally
{
foreach (var enumerator in enumerators)
{
enumerator?.Dispose();
}
}
}
}
And now you can merge an additional list
List<string> list1 = new List<string> { "rabbit", "dog", "cat", "shark" };
List<string> list2 = new List<string> { "red", "blue", "green", "black" };
List<string> list3 = new List<string> { "tiny", "medium", "small", "huge", "none" };
//rabbit, red, dog, blue, cat, green, shark, black
Console.WriteLine(string.Join(", ", list1.Merge(list2)));
//rabbit, red, tiny, dog, blue, medium, cat, green, small, shark, black, huge, none
Console.WriteLine(string.Join(", ", list1.Merge(list2, list3)));
Next you could add additional overloads with selectors and so on.