Zip N IEnumerable 一起? 同时迭代它们?

我有:-

IEnumerable<IEnumerable> items; 

而且我想创造: –

 IEnumerable<IEnumerable> results; 

其中“结果”中的第一项是“项目”的每个IEnumebles的第一项的IEnumerable,“结果”中的第二项是每个“项目”的第二项的IEnumerable等。

IEnumerables不一定是相同的长度。 如果项目中的某些IEnumerables在特定索引中没有元素,那么我希望结果中匹配的IEnumerable中包含更少的项目。

例如:-

 items = { "1", "2", "3", "4" } , { "a", "b", "c" }; results = { "1", "a" } , { "2", "b" }, { "3", "c" }, { "4" }; 

编辑:另一个例子(评论中要求): –

 items = { "1", "2", "3", "4" } , { "a", "b", "c" }, { "p", "q", "r", "s", "t" }; results = { "1", "a", "p" } , { "2", "b", "q" }, { "3", "c", "r" }, { "4", "s" }, { "t" }; 

我事先并不知道有多少序列,也没有每个序列中有多少元素。 我可能有1,000个序列,每个序列有1,000,000个元素,我可能只需要第一个~10个,所以如果可以的话,我想使用源序列的(懒惰)枚举。 特别是如果我能帮助它,我不想创建新的数据结构。

是否有内置方法(类似于IEnumerable.Zip)可以做到这一点?

还有另一种方式吗?

现在经过轻微测试并具有工作处理能力。

 public static class Extensions { public static IEnumerable> JaggedPivot( this IEnumerable> source) { List> originalEnumerators = source .Select(x => x.GetEnumerator()) .ToList(); try { List> enumerators = originalEnumerators .Where(x => x.MoveNext()).ToList(); while (enumerators.Any()) { List result = enumerators.Select(x => x.Current).ToList(); yield return result; enumerators = enumerators.Where(x => x.MoveNext()).ToList(); } } finally { originalEnumerators.ForEach(x => x.Dispose()); } } } public class TestExtensions { public void Test1() { IEnumerable> myInts = new List>() { Enumerable.Range(1, 20).ToList(), Enumerable.Range(21, 5).ToList(), Enumerable.Range(26, 15).ToList() }; foreach(IEnumerable x in myInts.JaggedPivot().Take(10)) { foreach(int i in x) { Console.Write("{0} ", i); } Console.WriteLine(); } } } 

如果您能保证结果将如何使用,那么这是相当简单的做法。 但是,如果结果可能以任意顺序使用,则可能需要缓冲所有内容。 考虑一下:

 var results = MethodToBeImplemented(sequences); var iterator = results.GetEnumerator(); iterator.MoveNext(); var first = iterator.Current; iterator.MoveNext(); var second = iterator.Current; foreach (var x in second) { // Do something } foreach (var x in first) { // Do something } 

为了获得“秒”中的项目,您必须遍历所有子序列, 超过第一项。 如果您希望它首先迭代项目是有效的,您需要记住项目准备重新评估子序列。

同样,您需要将子序列缓冲为IEnumerable值,或者每次重读整个批次。

基本上它是一整套蠕虫,很难以优雅的方式在所有情况下都能很好地工作:(如果你有适当的约束条件,我们可以提供更多帮助。

基于David B的答案 ,此代码应该表现更好:

 public static IEnumerable> JaggedPivot( this IEnumerable> source) { var originalEnumerators = source.Select(x => x.GetEnumerator()).ToList(); try { var enumerators = new List>(originalEnumerators.Where(x => x.MoveNext())); while (enumerators.Any()) { yield return enumerators.Select(x => x.Current).ToList(); enumerators.RemoveAll(x => !x.MoveNext()); } } finally { originalEnumerators.ForEach(x => x.Dispose()); } } 

不同之处在于,枚举器变量不会一直重新创建。

这是一个有点短,但无疑效率较低的一个:

 Enumerable.Range(0,items.Select(x => x.Count()).Max()) .Select(x => items.SelectMany(y => y.Skip(x).Take(1))); 

那这个呢?

  List items = new List() { new string[] { "a", "b", "c" }, new string[] { "1", "2", "3" }, new string[] { "x", "y" }, new string[] { "y", "z", "w" } }; var x = from i in Enumerable.Range(0, items.Max(a => a.Length)) select from z in items where z.Length > i select z[i]; 

你可以组合像这样的现有运营商,

 IEnumerable> myInts = new List>() { Enumerable.Range(1, 20).ToList(), Enumerable.Range(21, 5).ToList(), Enumerable.Range(26, 15).ToList() }; myInts.SelectMany(item => item.Select((number, index) => Tuple.Create(index, number))) .GroupBy(item => item.Item1) .Select(group => group.Select(tuple => tuple.Item2));