Interleaved与LINQ合并?

Tea*_*olf 28 c# linq

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

var first = new string[] { "1", "2", "3" };
var second = new string[] { "a", "b", "c" };
Run Code Online (Sandbox Code Playgroud)

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

"1", "a", "2", "b", "3", "c"
Run Code Online (Sandbox Code Playgroud)

到目前为止,我想出的是一个Zip匿名类型和SelectMany:

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

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

Dou*_*las 29

警告:如果枚举具有不同的长度,这将跳过尾随元素.如果您宁愿用空值替换填充较短的集合,请使用Andrew Shepherd的答案.


您可以编写自己的Interleave扩展方法,就像在此示例中一样.

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我想如果第一个集合更大,这个代码仍然会返回它们,但是如果第二个集合更大,它会跳过它们.也许值得继续通过while循环后的第二个集合的一致性:-) (2认同)

And*_*erd 28

通过免除匿名类型,您提供的示例可以更简单:

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

  • 如果第一个序列有 2 个元素,第二个序列有 1 个元素,那么这将产生 2 个元素。请小心此实现 - 它不是 2 个列表的真正合并。 (2认同)

Jul*_*ner 9

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

从一种Interleave方法我可以期望得到的序列包含

  1. 只有'对'(结果序列的长度:) min(length_1, length_2) * 2),或者那个
  2. 始终附加较长序列的其余元素(结果序列的长度:) length_1 + length_2.

以下实现遵循第二种方法.
注意|或者比较中的单个,这避免了短路评估.

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;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)


Guf*_*ffa 5

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

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