使用LINQ交错多个(超过2个)不规则列表
说我有以下数据
IEnumerable<IEnumerable> items = new IEnumerable[] { new int[] { 1, 2, 3, 4 }, new int[] { 5, 6 }, new int[] { 7, 8, 9 } };
返回包含交错项的平面列表的最简单方法是什么,以便得到结果:
1,5,7,2,6,8,3,9,4
注意:运行时不知道内部列表的数量。
您所描述的内容本质上是一种转置方法 ,其中包含悬垂项目并且结果被展平 。 这是我的尝试:
static IEnumerable> TransposeOverhanging( this IEnumerable> 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()); } }
例:
var result = items.TransposeOverhanging().SelectMany(g => g).ToList(); // result == { 1, 5, 7, 2, 6, 8, 3, 9, 4 }
以下解决方案非常直接。 事实certificate,它的速度几乎是dtb提出的解决方案的两倍。
private static IEnumerable Interleave (this IEnumerable> source ) { var queues = source.Select(x => new Queue (x)).ToList(); while (queues.Any(x => x.Any())) { foreach (var queue in queues.Where(x => x.Any())) { yield return queue.Dequeue(); } } }
根据dtb的回答 ,这是我的尝试。 它避免了外部SelectMany
和内部ToArray
调用。
public static IEnumerable Interleave (this IEnumerable> 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()); } }
- 处理所有枚举器,即使抛出exception也是如此
- 急切地评估外部序列,但对内部序列使用惰性评估。
public static IEnumerable Interleave (IEnumerable> sequences) { var enumerators = new List>(); 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>(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(); } } }
虽然它没有“dtb”的答案那么优雅,但它也有效,它的单线程:)
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)); });