我有: -
IEnumerable<IEnumerable<T>> items;
我想创建: -
IEnumerable<IEnumerable<T>> results;
其中“results”中的第一项是“items”的每个IEnumebles的第一项的IEnumerable,“results”中的第二项是每个“items”的第二项的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" };
我事先并不知道有多少序列,也不知道每个序列中有多少元素。我可能有1000个序列,每个序列有1,000,000个元素,我可能只需要第一个~10个,所以我想使用源序列的(惰性)枚举,如果可以的话。特别是如果我能帮助它,我不想创建新的数据结构。
是否有可以执行此操作的内置方法(类似于IEnumerable.Zip)?
还有其他办法吗?
答案 0 :(得分:7)
现在进行了轻度测试并进行了处理。
public static class Extensions
{
public static IEnumerable<IEnumerable<T>> JaggedPivot<T>(
this IEnumerable<IEnumerable<T>> source)
{
List<IEnumerator<T>> originalEnumerators = source
.Select(x => x.GetEnumerator())
.ToList();
try
{
List<IEnumerator<T>> enumerators = originalEnumerators
.Where(x => x.MoveNext()).ToList();
while (enumerators.Any())
{
List<T> 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<IEnumerable<int>> myInts = new List<IEnumerable<int>>()
{
Enumerable.Range(1, 20).ToList(),
Enumerable.Range(21, 5).ToList(),
Enumerable.Range(26, 15).ToList()
};
foreach(IEnumerable<int> x in myInts.JaggedPivot().Take(10))
{
foreach(int i in x)
{
Console.Write("{0} ", i);
}
Console.WriteLine();
}
}
}
答案 1 :(得分:4)
如果您可以保证结果将如何使用,那么可以合理地直接进行。但是,如果结果可能以任意顺序使用,则可能需要缓冲所有内容。考虑一下:
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
}
为了获得“秒”中的项目,您必须遍历所有子序列,过去第一项。如果您希望它有效迭代first
中的项目,则
同样,您需要将子序列缓冲为IEnumerable<T>
值,或者每次重读整个批次。
基本上它是一整套蠕虫,很难以优雅的方式在所有情况下都能很好地工作:(如果你有适当的约束条件的特定的情况,我们可能会能够提供更多帮助。
答案 2 :(得分:1)
基于David B's answer,此代码的效果应该更好:
public static IEnumerable<IEnumerable<T>> JaggedPivot<T>(
this IEnumerable<IEnumerable<T>> source)
{
var originalEnumerators = source.Select(x => x.GetEnumerator()).ToList();
try
{
var enumerators =
new List<IEnumerator<T>>(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());
}
}
不同之处在于,枚举器变量不会一直重新创建。
答案 3 :(得分:0)
这是一个有点短,但无疑效率较低的一个:
Enumerable.Range(0,items.Select(x => x.Count()).Max())
.Select(x => items.SelectMany(y => y.Skip(x).Take(1)));
答案 4 :(得分:0)
这个怎么样?
List<string[]> items = new List<string[]>()
{
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];
答案 5 :(得分:0)
你可以组合像这样的现有运营商,
IEnumerable<IEnumerable<int>> myInts = new List<IEnumerable<int>>()
{
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));