2011-12-15 111 views
20

我知道這個問題以前已經問過很多次了,但我試過了答案,但它們似乎並不奏效。如何通過使用一個foreach迭代兩個相同長度的集合

我有兩個相同長度但不是相同類型的列表,我想在list1 [i]連接到list2 [i]的同時迭代它們兩個。

如:

假設我有列表1(如表)和List2(如表)

我想去做

foreach(var listitem1, listitem2 in list1, list2) 
    { 
     //do stuff 
    } 

soemthing這是possbile?

+0

什麼在第一個foreach中有第二個列表的foreach? – 2011-12-15 10:44:26

+0

Dupe:http://stackoverflow.com/questions/5630758/how-to-loop-through-two-lists-simultaneously – Jonathan 2011-12-15 10:47:12

回答

28

編輯 - 迭代而在兩個集合在同一指數在定位

如果需求是通過兩個集合在一個「同步」的方式移動,即使用第一個集合的第一個元素與第二個集合的第一個元素,然後是第二個集合,依此類推,而不需要執行任何副作用代碼,然後參見@sll's answer並使用.Zip()在同一索引處投影出對元素,直到其中一個集合用盡元素。

更普遍

取而代之的是foreach的,你可以從兩個集合的IEnumerable使用GetEnumerator()方法訪問IEnumerator,然後調用MoveNext()的集合,當你需要在移動到下一個元素那個集合。這種技術在處理兩個或更多有序流時是常見的,而不需要實現流。

var stream1Enumerator = stream1.GetEnumerator(); 
var stream2Enumerator = stream2.GetEnumerator(); 
// i.e. Until stream1Enumerator runs out of 
while (stream1Enumerator.MoveNext()) 
{ 
    // Now you can iterate the collections independently 
    if (moon == 'Blue') 
    { 
     stream2Enumerator.MoveNext(); 
    } 
    // Do something with stream1Enumerator.Current and stream2Enumerator.Current 
} 

正如其他人所指出的,如果集合被物化和支持索引,如ICollection接口,您也可以使用標[]運營商,雖然這種感覺時下相當笨拙:

var smallestUpperBound = Math.Min(collection1.Count, collection2.Count); 
for (var index = 0; index < smallestUpperBound; index++) 
{ 
    // Do something with collection1[index] and collection2[index] 
} 

最後,還有一個overload of Linq's .Select(),它提供返回的元素的索引序號,這也可能有用。

例如下面將配對的collection1所有元素或者與collection2前兩個元素:

var alternatePairs = collection1.Select(
    (item1, index1) => new 
    { 
     Item1 = item1, 
     Item2 = collection2[index1 % 2] 
    }); 
15

簡短的回答是不,你不能。

較長的答案是,因爲foreach是語法糖 - 它從集合中獲取一個迭代器並在其上調用Next。這是不可能與兩個集合在同一時間。

如果您只想要一個循環,則可以使用for循環,併爲這兩個集合使用相同的索引值。

for(int i = 0; i < collectionsLength; i++) 
{ 
    list1[i]; 
    list2[i]; 
} 

一個替代方案是兩個集合合併成一個使用LINQ Zip操作者(新到.NET 4.0)和迭代的結果。

38

這是可能的使用。NET 4 LINQ Zip() operator或者使用開放源碼MoreLINQ library提供Zip()操作以及因此可以用它在多個較早版本的.NET

實施例從MSDN

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

// The following example concatenates corresponding elements of the 
// two input sequences. 
var numbersAndWords = numbers.Zip(words, (first, second) => first + " " + second); 
foreach (var item in numbersAndWords) 
{ 
    Console.WriteLine(item); 
} 

// OUTPUT: 
// 1 one 
// 2 two 
// 3 three 

有用鏈接:

2

而不是foreach,爲什麼不使用for()?例如...

int length = list1.length; 
for(int i = 0; i < length; i++) 
{ 
    // do stuff with list1[i] and list2[i] here. 
} 
4
foreach(var tup in list1.Zip(list2, (i1, i2) => Tuple.Create(i1, i2))) 
{ 
    var listItem1 = tup.Item1; 
    var listItem2 = tup.Item2; 
    /* The "do stuff" from your question goes here */ 
} 

它雖然可以是這樣的,你很多「做的東西」可以在這裏創建了一個拉姆達去元組,這會更好。

如果這些集合是可以迭代的,那麼for()循環可能更簡單。

更新:現在有內置在C#7.0 ValueTuple的支持,我們可以使用:

foreach ((var listitem1, var listitem2) in list1.Zip(list2, (i1, i2) => (i1, i2))) 
{ 
    /* The "do stuff" from your question goes here */ 
} 
0

你可以用兩個IEnumerable的<>在輔助類:

var nums = new []{1, 2, 3}; 
var strings = new []{"a", "b", "c"}; 

ForEach(nums, strings).Do((n, s) => 
{ 
    Console.WriteLine(n + " " + s); 
}); 

//----------------------------- 

public static TwoForEach<A, B> ForEach<A, B>(IEnumerable<A> a, IEnumerable<B> b) 
{ 
    return new TwoForEach<A, B>(a, b); 
} 

public class TwoForEach<A, B> 
{ 
    private IEnumerator<A> a; 
    private IEnumerator<B> b; 

    public TwoForEach(IEnumerable<A> a, IEnumerable<B> b) 
    { 
    this.a = a.GetEnumerator(); 
    this.b = b.GetEnumerator(); 
    } 

    public void Do(Action<A, B> action) 
    { 
    while (a.MoveNext() && b.MoveNext()) 
    { 
     action.Invoke(a.Current, b.Current); 
    } 
    } 
} 
相關問題