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

时间:2009-03-25 18:38:24

标签: c# .net linq ienumerable

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

10 个答案:

答案 0 :(得分:17)

注意:从.NET 4.0开始,该框架在IEnumerable上包含.Zip扩展方法,记录为here。以下内容适用于后代,适用于早于4.0的.NET framework版本。

我使用这些扩展方法:

// 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<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> 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<KeyValuePair<T, R>> Zip<T, R>(this IEnumerable<T> first, IEnumerable<R> second) {
    return first.Zip(second, (f, s) => new KeyValuePair<T, R>(f, s));
}

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

  • 我最初从Bart De Smet's blog
  • 逐字地执行了第一个Zip实现
  • 添加了枚举器处理(Bart的原始帖子也是noted
  • 添加了空参数检查(也在Bart的帖子中讨论过)

答案 1 :(得分:15)

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

int[] numbers = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three" };

var numbersAndWords = numbers.Zip(words, (first, second) => first + " " + second);

答案 2 :(得分:2)

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

你想组合两个IEnumerables,其中“KeyValuePair中连接在一起的元素的索引是相同的”,但你“没有计数或 索引

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

答案 3 :(得分:1)

查看nextension

  

目前已实施的方法

     

的IEnumerable

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

答案 4 :(得分:1)

我会使用类似的东西 -

IEnumerable<KeyValuePair<T,U>> Merge<T,U>(IEnumerable<T> keyCollection, IEnumerable<U> valueCollection)
{
    var keys = keyCollection.GetEnumerator();
    var values = valueCollection.GetEnumerator();
    try
    { 
        keys.Reset();
        values.Reset();

        while (keys.MoveNext() && values.MoveNext())
        {
            yield return new KeyValuePair<T,U>(keys.Current,values.Current);
        }
    }
    finally
    {
        keys.Dispose();
        values.Dispose();
    }
}

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

答案 5 :(得分:0)

未经测试,但应该有效:

IEnumerable<KeyValuePair<T, U>> Zip<T, U>(IEnumerable<T> t, IEnumerable<U> u) {
    IEnumerator<T> et = t.GetEnumerator();
    IEnumerator<U> 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<T, U>(et.Current, eu.Current);
    }
}

答案 6 :(得分:0)

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

答案 7 :(得分:0)

MSDN具有以下Custom Sequence Operators示例。而Welbog是对的;如果您没有基础数据的索引,则无法保证操作符合您的预期。

答案 8 :(得分:0)

JaredPar有一个library,其中包含许多有用的内容,包含Zip,可以启用您想要的内容。

答案 9 :(得分:0)

Alexey Romanov的functional-dotnet project的另一个实现:

/// <summary>
/// Takes two sequences and returns a sequence of corresponding pairs. 
/// If one sequence is short, excess elements of the longer sequence are discarded.
/// </summary>
/// <typeparam name="T1">The type of the 1.</typeparam>
/// <typeparam name="T2">The type of the 2.</typeparam>
/// <param name="sequence1">The first sequence.</param>
/// <param name="sequence2">The second sequence.</param>
/// <returns></returns>
public static IEnumerable<Tuple<T1, T2>> Zip<T1, T2>(
    this IEnumerable<T1> sequence1, IEnumerable<T2> sequence2) {
    using (
        IEnumerator<T1> enumerator1 = sequence1.GetEnumerator())
    using (
        IEnumerator<T2> 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<T1, T2>(以及返回类型),您就可以了。