2011-08-28 46 views
25

我目前正在试验一下LINQ。比方说,我有相同长度的两个集合:Interleaved与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实现这种交错合并的方法?

回答

23

警告:如果枚举的长度不同,这将跳过尾随元素。如果您想用空值代替较短的收藏,请使用下面的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; 
     } 
    } 
} 
+0

当然,在可重用性和可读性方面都是一个很好的解决方案。 – TeaWolf

+2

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

+0

@丹尼,是的。我的意愿是,一旦最短的时间内停止,那么你不必担心如何填补空白。(通常我不会把其他人的代码放在我的答案中,但是我将离开ChaosPandion的编辑,单独使用Jiri的代码。) – Douglas

25

您提供可以在例如用匿名类型的分配变得简单:

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

感谢您的简化,看起来我再次以更难的方式做事。 – TeaWolf

+0

我看不出比这更简单:) – MBen

+1

如果第一个序列有2个元素,第二个序列有1个元素,那么这将产生2个元素。注意这个实现 - 它不是2个列表的真正合并。 – Denis

4

你可以循环,并选择阵列取决于指数:

var result = 
    Enumerable.Range(0, first.Length * 2) 
    .Select(i => (i % 2 == 0 ? first : second)[i/2]); 
1
var result = first.SelectMany((f, i) => new List<string> { f, second[ i ] }); 
5

在接受的答案中给出的实现具有不一致性:
生成的序列将始终包含第一个序列的所有元素(因为外部的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; 
    } 
    } 
}