2011-08-28 44 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; 
    } 
    } 
}