假设我有两个枚举,我知道它们具有相同数量的元素,并且每个元素与另一个枚举中相同位置的元素“对应”。有没有办法同时处理这两个枚举,以便我可以同时访问每个枚举的相应元素?
使用理论上的LINQ语法,我想到的是:
from x in seq1, y in seq2
select new {x.foo, y.bar}
答案 0 :(得分:3)
您正在寻找的功能称为“Zip”。它像拉链一样工作。它将在.NET 4.0 iirc中。在此期间,您可能需要查看BclExtras库。 (伙计,我是这个lib的真正拥护者,哈哈)。
IEnumerable<Tuple<TSeq1, TSeq2>> tuples = from t in seq1.Zip(seq2)
select t;
如果你只是想完成,你必须得到两个序列枚举器并使用传统循环“并行”运行它们。
答案 1 :(得分:3)
由于Neil Williams删除了他的答案,我会继续发布implementation by Jon Skeet的链接。
解释相关部分:
public static IEnumerable<KeyValuePair<TFirst,TSecond>> Zip<TFirst,TSecond>
(this IEnumerable<TFirst> source, IEnumerable<TSecond> secondSequence)
{
using (IEnumerator<TSecond> secondIter = secondSequence.GetEnumerator())
{
foreach (TFirst first in source)
{
if (!secondIter.MoveNext())
{
throw new ArgumentException
("First sequence longer than second");
}
yield return new KeyValuePair<TFirst, TSecond>(first, secondIter.Current);
}
if (secondIter.MoveNext())
{
throw new ArgumentException
("Second sequence longer than first");
}
}
}
请注意KeyValuePair<>
是我的补充,我通常不喜欢这种方式使用它。相反,我会定义通用的Pair
或Tuple
类型。但是,它们不包含在当前版本的框架中,我不想使用额外的类定义来混淆这个示例。
答案 2 :(得分:0)
在4.0中添加了一个“Zip”方法来解决这个问题(就像一个拉链,拉紧相邻的元素。)在此之前,最可读的(虽然不是最佳的)方式可能是这样的,除非懒惰的评估非常重要:
var indexedA = seqA.ToArray();
var indexedB = seqB.ToArray();
for(int i = 0; i < indexedA.Length && i < indexedB.Length; i++)
{
var thisA = indexedA[i];
var thisB = indexedB[i];
// whatever
}
答案 3 :(得分:-2)
<强>更新强>
Eric Lippert最近发布了这个帖子:http://blogs.msdn.com/ericlippert/archive/2009/05/07/zip-me-up.aspx
这特别有趣,因为他在C#4中发布了新扩展的来源:
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>
(this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TSecond, TResult> resultSelector)
{
if (first == null) throw new ArgumentNullException("first");
if (second == null) throw new ArgumentNullException("second");
if (resultSelector == null) throw new ArgumentNullException("resultSelector");
return ZipIterator(first, second, resultSelector);
}
private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>
(IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TSecond, TResult> resultSelector)
{
using (IEnumerator<TFirst> e1 = first.GetEnumerator())
using (IEnumerator<TSecond> e2 = second.GetEnumerator())
while (e1.MoveNext() && e2.MoveNext())
yield return resultSelector(e1.Current, e2.Current);
}
原始回答:
您指的是加入吗?
from x in seq1
join y in seq2
on x.foo equals y.foo
select new {x, y}
还有pLinq - 并行执行linq语句(跨多个线程)。
修改强>
啊 - 谢谢你澄清这个问题,虽然我真的认为我的答案不值得投票。听起来你想要的是:
from x in seq1
join y in seq2
on x.Index equals y.Index
select new {x.Foo, y.Bar}
不幸的是,你不能用Linq做到这一点 - 它扩展了IEnumerable
,它只有current
和next
属性,因此没有索引属性。
显然你可以在C#中使用嵌套的for循环和if块轻松地完成这项工作,但你不能用Linq我不敢。
在linq语法中模仿这种方法的唯一方法是人为添加索引:
int counter = 0;
var indexed1 = (
from x in seq1
select { item = x, index = counter++ } ).ToList();
//note the .ToList forces execution, this won't work if lazy
counter = 0;
var indexed2 = (
from x in seq2
select { item = x, index = counter++ } ).ToList();
var result =
from x in indexed1
join y in indexed2
on x.index = y.index
select new {x.item.Foo, y.item.Bar}