簡體   English   中英

如何使用單個foreach迭代兩個相同長度的集合

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM