[英]How to iterate through two collections of the same length using a single foreach
我知道這個問題之前已被問過多次,但我嘗試了答案,但它們似乎沒有用。
我有兩個長度相同但類型不同的列表,我想在list1[i]
連接到list2[i]
的同時迭代它們。
例如:
假設我有list1(作為List<string>
)和list2(作為List<int>
)
我想做點什么
foreach( var listitem1, listitem2 in list1, list2)
{
// do stuff
}
這可能嗎?
這可以使用.NET 4 LINQ Zip()運算符或使用提供Zip()
運算符的開源MoreLINQ庫 ,因此您可以在更早期的.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
有用的鏈接:
編輯 - 在兩個集合中的同一索引處進行迭代
如果要求是以“同步”方式移動兩個集合,即將第一個集合的第一個元素與第二個集合的第一個元素一起使用,那么將第二個元素與第二個集合一起使用,依此類推,而不需要執行任何一方影響代碼,然后查看@sll的答案並使用.Zip()
在同一索引處投射出元素對,直到其中一個集合用盡元素。
更普遍
您可以使用GetEnumerator()
方法從兩個集合的IEnumerable
中訪問IEnumerator
,而不是foreach
,然后在需要移動到該集合中的下一個元素時MoveNext()
集合上的MoveNext()
。 當處理兩個或更多個有序流時,這種技術很常見,而不需要實現流。
var stream1Enumerator = stream1.GetEnumerator();
var stream2Enumerator = stream2.GetEnumerator();
var currentGroupId = -1; // Initial value
// i.e. Until stream1Enumerator runs out of
while (stream1Enumerator.MoveNext())
{
// Now you can iterate the collections independently
if (stream1Enumerator.Current.Id != currentGroupId)
{
stream2Enumerator.MoveNext();
currentGroupId = stream2Enumerator.Current.Id;
}
// Do something with stream1Enumerator.Current and stream2Enumerator.Current
}
正如其他人所指出的那樣,如果集合具體化並支持索引,例如ICollection
接口,你也可以使用subscript []
運算符,盡管現在感覺相當笨拙:
var smallestUpperBound = Math.Min(collection1.Count, collection2.Count);
for (var index = 0; index < smallestUpperBound; index++)
{
// Do something with collection1[index] and collection2[index]
}
最后,還有Linq的.Select()
重載,它提供了返回元素的索引序號,這也很有用。
例如,下面將配對的所有元素collection1
可選地與前兩個元素collection2
:
var alternatePairs = collection1.Select(
(item1, index1) => new
{
Item1 = item1,
Item2 = collection2[index1 % 2]
});
簡短的回答是不,你不能。
更長的答案是因為foreach
是語法糖 - 它從集合中獲取迭代器並在其上調用Next
。 這兩個集合不可能同時進行。
如果您只想擁有一個循環,則可以使用for
循環並對兩個集合使用相同的索引值。
for(int i = 0; i < collectionsLength; i++)
{
list1[i];
list2[i];
}
另一種方法是使用LINQ Zip運算符(.NET 4.0的新增功能)將兩個集合合並為一個集合並迭代結果。
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 */
}
雖然可以這樣你的“做東西”可以放在lambda中,這里創建一個元組,這會更好。
如果集合是可以迭代的,那么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 */
}
您可以將兩個IEnumerable <>包裝在helper類中:
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);
}
}
}
而不是foreach,為什么不使用for()? 例如...
int length = list1.length;
for(int i = 0; i < length; i++)
{
// do stuff with list1[i] and list2[i] here.
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.