如何将两个IEnumerables合并(或压缩)?

我有一个IEnumerable和一个IEnumerable我想要合并到IEnumerable<KeyValuePair> ,其中KeyValuePair中连接在一起的元素的索引是相同的。 注意我没有使用IList,所以我没有计算我正在合并的项目或索引。 我怎样才能做到最好? 我更喜欢LINQ的答案,但任何能够以优雅的方式完成工作的东西都会起作用。

注意:从.NET 4.0开始,框架在IEnumerable上包含一个.Zip扩展方法, 在此处记录 。 以下内容针对后代版本以及在早于4.0的.NET框架版本中使用。

我使用这些扩展方法:

 // From http://community.bartdesmet.net/blogs/bart/archive/2008/11/03/c-4-0-feature-focus-part-3-intermezzo-linq-s-new-zip-operator.aspx public static IEnumerable Zip(this IEnumerable first, IEnumerable second, Func func) { if (first == null) throw new ArgumentNullException("first"); if (second == null) throw new ArgumentNullException("second"); if (func == null) throw new ArgumentNullException("func"); using (var ie1 = first.GetEnumerator()) using (var ie2 = second.GetEnumerator()) while (ie1.MoveNext() && ie2.MoveNext()) yield return func(ie1.Current, ie2.Current); } public static IEnumerable> Zip(this IEnumerable first, IEnumerable second) { return first.Zip(second, (f, s) => new KeyValuePair(f, s)); } 

编辑 :在评论之后,我不得不澄清并解决一些问题:

  • 我最初从Bart De Smet的博客中逐字逐句实施了Zip
  • 添加了枚举器处理(在Bart的原始post中也有注明 )
  • 添加了空参数检查(也在Bart的post中讨论过)

作为对这个问题磕磕绊绊的人的更新,.Net 4.0本身就支持这个来自MS:

 int[] numbers = { 1, 2, 3, 4 }; string[] words = { "one", "two", "three" }; var numbersAndWords = numbers.Zip(words, (first, second) => first + " " + second); 

想想你在这里要求的更多内容:

你想要结合两个IEnumerables,其中“KeyValuePair中元素的索引是相同的”,但你“ 没有我正在合并的项目的计数或 索引 ”。

无法保证您的IEnumerables甚至排序或未排序。 您的两个IEnumerable对象之间没有相关性,那么您如何期望将它们关联起来呢?

看看nextension :

目前已实施的方法

IEnumerable的

  • ForEach对IEnumerable的每个元素执行指定的操作。
  • Clump将项目分组为相同大小的批次。
  • 扫描通过将委托应用于IEnumerable中的项目对来创建列表。
  • AtLeast检查IEnumerable中至少有一定数量的项目。
  • AtMost检查IEnumerable中的项目数量不超过一定数量。
  • Zip通过将两个其他列表合并为一个列表来创建列表。
  • 循环通过重复另一个列表创建列表。

我会用以下的东西 –

 IEnumerable> Merge(IEnumerable keyCollection, IEnumerable valueCollection) { var keys = keyCollection.GetEnumerator(); var values = valueCollection.GetEnumerator(); try { keys.Reset(); values.Reset(); while (keys.MoveNext() && values.MoveNext()) { yield return new KeyValuePair(keys.Current,values.Current); } } finally { keys.Dispose(); values.Dispose(); } } 

这应该正常工作,然后正确清理。

未经测试,但应该工作:

 IEnumerable> Zip(IEnumerable t, IEnumerable u) { IEnumerator et = t.GetEnumerator(); IEnumerator eu = u.GetEnumerator(); for (;;) { bool bt = et.MoveNext(); bool bu = eu.MoveNext(); if (bt != bu) throw new ArgumentException("Different number of elements in t and u"); if (!bt) break; yield return new KeyValuePair(et.Current, eu.Current); } } 

您可以在MoreLINQ中使用Zip方法。

MSDN具有以下自定义序列运算符示例。 而Welbog是对的; 如果您没有基础数据的索引,则无法保证操作符合您的预期。

Alexey Romanov的function性网络项目的另一个实现:

 ///  /// Takes two sequences and returns a sequence of corresponding pairs. /// If one sequence is short, excess elements of the longer sequence are discarded. ///  /// The type of the 1. /// The type of the 2. /// The first sequence. /// The second sequence. ///  public static IEnumerable> Zip( this IEnumerable sequence1, IEnumerable sequence2) { using ( IEnumerator enumerator1 = sequence1.GetEnumerator()) using ( IEnumerator enumerator2 = sequence2.GetEnumerator()) { while (enumerator1.MoveNext() && enumerator2.MoveNext()) { yield return Pair.New(enumerator1.Current, enumerator2.Current); } } // //zip :: [a] -> [b] -> [(a,b)] //zip (a:as) (b:bs) = (a,b) : zip as bs //zip _ _ = [] } 

Pair.New替换为新的KeyValuePair (以及返回类型),你就可以了。

JaredPar有一个包含很多有用内容的库 ,包括Zip ,它可以实现你想做的事情。