Moving multiple (over 2) irregular lists using LINQ

Say I have the following data

IEnumerable<IEnumerable<int>> items = new IEnumerable<int>[] { new int[] { 1, 2, 3, 4 }, new int[] { 5, 6 }, new int[] { 7, 8, 9 } }; 

What would be the easiest way to return a flat list with elements alternating so that I get the result:

1, 5, 7, 2, 6, 8, 3, 9, 4

Note. The number of internal lists is unknown at runtime.

+7
source share
5 answers

What you are describing is basically a transpose method , which includes supercharged elements and the result is flattened . Here is my attempt:

 static IEnumerable<IEnumerable<T>> TransposeOverhanging<T>( this IEnumerable<IEnumerable<T>> source) { var enumerators = source.Select(e => e.GetEnumerator()).ToArray(); try { T[] g; do { yield return g = enumerators .Where(e => e.MoveNext()).Select(e => e.Current).ToArray(); } while (g.Any()); } finally { Array.ForEach(enumerators, e => e.Dispose()); } } 

Example:

 var result = items.TransposeOverhanging().SelectMany(g => g).ToList(); // result == { 1, 5, 7, 2, 6, 8, 3, 9, 4 } 
+10
source

The solution below is very straight forward. As it turned out, it is also almost twice as fast as the solution proposed by dtb.

 private static IEnumerable<T> Interleave<T>(this IEnumerable<IEnumerable<T>> source ) { var queues = source.Select(x => new Queue<T>(x)).ToList(); while (queues.Any(x => x.Any())) { foreach (var queue in queues.Where(x => x.Any())) { yield return queue.Dequeue(); } } } 
+2
source

Here is my attempt based on dtb answer . It avoids external calls to SelectMany and internal ToArray .

 public static IEnumerable<T> Interleave<T>(this IEnumerable<IEnumerable<T>> source) { var enumerators = source.Select(e => e.GetEnumerator()).ToArray(); try { bool itemsRemaining; do { itemsRemaining = false; foreach (var item in enumerators.Where(e => e.MoveNext()).Select(e => e.Current)) { yield return item; itemsRemaining = true; } } while (itemsRemaining); } finally { Array.ForEach(enumerators, e => e.Dispose()); } } 
+1
source
  • All counters are disposed even if exceptions are thrown
  • Satisfies the outer sequence, but uses a lazy rating for inner sequences.

 public static IEnumerable<T> Interleave<T>(IEnumerable<IEnumerable<T>> sequences) { var enumerators = new List<IEnumerator<T>>(); try { // using foreach here ensures that `enumerators` contains all already obtained enumerators, in case of an expection is thrown here. // this ensures proper disposing in the end foreach(var enumerable in sequences) { enumerators.Add(enumerable.GetEnumerator()); } var queue = new Queue<IEnumerator<T>>(enumerators); while (queue.Any()) { var enumerator = queue.Dequeue(); if (enumerator.MoveNext()) { queue.Enqueue(enumerator); yield return enumerator.Current; } } } finally { foreach(var enumerator in enumerators) { enumerator.Dispose(); } } } 
0
source

Although it is not as elegant as the answer "dtb", but it also works and its one liner :)

 Enumerable.Range(0, items.Max(x => x.Count())) .ToList() .ForEach(x => { items .Where(lstChosen => lstChosen.Count()-1 >= x) .Select(lstElm => lstElm.ElementAt(x)) .ToList().ForEach(z => Console.WriteLine(z)); }); 
-one
source

All Articles