Interleaved与LINQ合并?

时间:2011-08-28 22:49:51

标签: c# linq

我目前正在尝试使用LINQ。假设我有两个相同长度的集合:

var first = new string[] { "1", "2", "3" };
var second = new string[] { "a", "b", "c" };

我想将这两个集合合并为一个,但是以交错的方式。因此,结果序列应为:

"1", "a", "2", "b", "3", "c"

到目前为止,我提出的是Zip,匿名类型和SelectMany的组合:

var result = first.Zip( second, ( f, s ) => new { F = f, S = s } )
                  .SelectMany( fs => new string[] { fs.F, fs.S } );

有没有人知道用LINQ实现这种交错合并的替代/简单方法?

6 个答案:

答案 0 :(得分:29)

警告:如果枚举的长度不同,这将跳过尾随元素。如果您宁愿用空值替换填充较短的集合,请使用下面的Andrew Shepherd's answer


您可以编写自己的Interleave扩展方法,例如this example

internal static IEnumerable<T> InterleaveEnumerationsOfEqualLength<T>(
    this IEnumerable<T> first, 
    IEnumerable<T> second)
{
    using (IEnumerator<T>
        enumerator1 = first.GetEnumerator(),
        enumerator2 = second.GetEnumerator())
    {
        while (enumerator1.MoveNext() && enumerator2.MoveNext())
        {
            yield return enumerator1.Current;
            yield return enumerator2.Current;
        }
    }
}

答案 1 :(得分:27)

您提供的示例可以通过省略匿名类型来简化:

   var result = first.Zip(second, (f, s) => new[] { f, s })
                      .SelectMany(f => f);

答案 2 :(得分:9)

在接受的答案中给定的实施具有不一致性:
结果序列将始终包含第一个序列的所有元素(因为外部while循环),但如果第二个序列包含更多元素,则不会附加这些元素。

Interleave方法我希望结果序列包含

  1. 仅'对'(结果序列的长度:min(length_1, length_2) * 2)),或
  2. 始终附加较长序列的其余元素(结果序列的长度:length_1 + length_2)。
  3. 以下实施遵循第二种方法 请注意or-comparison中的单个|,以避免短路评估。

    public static IEnumerable<T> Interleave<T> (
        this IEnumerable<T> first, IEnumerable<T> second)
    {
      using (var enumerator1 = first.GetEnumerator())
      using (var enumerator2 = second.GetEnumerator())
      {
        bool firstHasMore;
        bool secondHasMore;
    
        while ((firstHasMore = enumerator1.MoveNext())
             | (secondHasMore = enumerator2.MoveNext()))
        {
          if (firstHasMore)
            yield return enumerator1.Current;
    
          if (secondHasMore)
            yield return enumerator2.Current;
        }
      }
    }
    

答案 3 :(得分:4)

您可以根据索引循环并选择数组:

var result =
  Enumerable.Range(0, first.Length * 2)
  .Select(i => (i % 2 == 0 ? first : second)[i / 2]);

答案 4 :(得分:2)

var result = first.SelectMany( ( f, i ) => new List<string> { f, second[ i ] } );

答案 5 :(得分:0)

这是@Douglas 答案的修改版本。这允许动态数量的 IEnumerable 交错,并遍历所有元素,而不是在最短的 IEnumerable 的末尾停止。

public static IEnumerable<T> Interleave<T>(params IEnumerable<T>[] enumerables)
        {
            var enumerators = enumerables.Select(e => e.GetEnumerator()).ToList();
            while (enumerators.Any())
            {
                enumerators.RemoveAll(e => {
                    var ended = !e.MoveNext();
                    if (ended) e.Dispose();
                    return ended;
                });

                foreach (var enumerator in enumerators)
                    yield return enumerator.Current;
            }
        }