【问题标题】:How do I merge (or zip) two IEnumerables together?如何将两个 IEnumerable 合并(或压缩)在一起?
【发布时间】:2010-10-15 12:53:38
【问题描述】:

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

【问题讨论】:

  • 仍然是 Eric Lippert 的 another blog post
  • 很有趣——我昨晚刚读到这个。 =)
  • 从 .NET 4.0 开始,该框架带有一个 IEnumerable Zip 扩展方法。

标签: c# .net linq ienumerable


【解决方案1】:

注意:从 .NET 4.0 开始,该框架在 IEnumerable 上包含一个 .Zip 扩展方法,文档化为 here。以下是为后代维护的,并在 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<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));
}

编辑:在 cmets 之后,我有义务澄清和解决一些问题:

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

【讨论】:

  • 排序:它鼓励调用者做出假设。这是它唯一能做的事情,有时这个假设是有根据的。
  • 你也应该处理你的枚举数。
  • 很公平。尽管如此,如果你不小心传递给它的类型,你会在脚上开枪。
  • @reed:查看 Bart 的原始文章 (community.bartdesmet.net/blogs/bart/archive/2008/11/03/…),它涵盖了处置和其他问题。
  • 谢谢 - 这正是我要找的。​​span>
【解决方案2】:

作为对遇到此问题的任何人的更新,.Net 4.0 原生支持此问题,例如来自 MS:

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

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

Documentation:

该方法将第一个序列的每个元素与第二个序列中具有相同索引的元素合并。如果序列没有相同数量的元素,则该方法会合并序列,直到它到达其中一个的末尾。例如,如果一个序列有三个元素,而另一个序列有四个,则结果序列将只有三个元素。

【讨论】:

    【解决方案3】:

    在这里更仔细地考虑一下您要问的问题:

    您想组合两个 IEnumerable,其中“在 KeyValuePair 中连接在一起的元素的 索引 相同”,但您“没有计数或 strong> index 用于我要合并的项目”。

    无法保证您的 IEnumerables 是排序的还是未排序的。你的两个 IEnumerable 对象之间没有相关性,那么你怎么能期望它们相关呢?

    【讨论】:

    • @welbog:看起来对这个问题有误解。我认为“索引”Erik 是指元素在 IEnumerable 中的位置(第 1、第 2 等)
    • @mausch:无法保证的位置。根据实现,两个 IEnumerable 的顺序可能与预期不同。
    • @welbog: 用这样的枚举来调用 Zip 有意义吗?要么没有意义,要么调用者必须意识到这一点......我没有看到任何其他选项。
    • @mausch:我的意思是问题本身无法按说明解决。在两个 IEnumerable 中的对象之间建立连接根本不需要任何信息。您的解决方案做了一个假设,一个添加额外信息的假设,但在错误时会崩溃和烧毁。
    • “你怎么能期望将它们关联起来” - 有效点,但我假设“索引”只是它们从 IEnumerable 产生的顺序。这就是我需要与我的情况相关的所有内容。
    【解决方案4】:

    nextension

    当前实现的方法

    IEnumerable

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

    【讨论】:

      【解决方案5】:

      我会使用类似-

      的东西
      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();
          }
      }
      

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

      【讨论】:

      • 我认为将其称为“zip”是一种很好的形式,因为这是函数世界中的已知操作。
      【解决方案6】:

      未经测试,但应该可以工作:

      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);
          }
      }
      

      【讨论】:

        【解决方案7】:

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

        【讨论】:

          【解决方案8】:

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

          【讨论】:

            【解决方案9】:

            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 _      _      = []
            }
            

            用新的KeyValuePair&lt;T1, T2&gt;(和返回类型)替换Pair.New,你就可以开始了。

            【讨论】:

              【解决方案10】:

              JaredPar 有一个library,其中包含很多有用的东西,包括Zip,它将启用您想要做的事情。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2023-01-12
                • 2014-01-16
                • 2011-02-15
                • 1970-01-01
                • 1970-01-01
                • 2011-04-28
                • 2016-11-23
                相关资源
                最近更新 更多