Подтвердить что ты не робот

Как выполнить итерацию по двум коллекциям одинаковой длины, используя один foreach

Я знаю, что этот вопрос задавался много раз, но я опробовал ответы, и они, похоже, не работают.

У меня есть два списка одинаковой длины, но не одного и того же типа, и я хочу перебирать их оба в то же самое время, когда list1 [i] подключен к списку2 [i].

Например:

Предполагая, что у меня есть list1 (как List) и list2 (as List)

Я хочу сделать что-то вроде

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

Возможно ли это?

4b9b3361

Ответ 1

Изменить - Итерация при позиционировании с одним индексом в обеих коллекциях

Если требование состоит в том, чтобы перемещать обе коллекции по принципу "синхронно", то есть использовать 1-й элемент первой коллекции с 1-м элементом второй коллекции, затем 2-й со 2-го и так далее, без необходимости выполнить какой-либо побочный код, затем посмотреть @sll answer и использовать .Zip() для проектирования пар элементов в одном и том же индексе, пока один из наборов не закончится из элементов.

Больше

Вместо foreach вы можете получить доступ к IEnumerator из IEnumerable обеих коллекций с помощью метода GetEnumerator(), а затем вызвать 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]
}

Наконец, существует также перегрузка Linq .Select(), которая предоставляет порядковый номер индекса возвращенного элемента, который также может быть полезен.

например. ниже будут объединены все элементы collection1 в качестве альтернативы с первыми двумя элементами collection2:

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

Ответ 2

Это возможно с помощью .NET 4 LINQ оператора Zip() или с использованием библиотеки с расширением openLINQ с открытым исходным кодом, которая предоставляет оператор 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

Полезные ссылки:

Ответ 3

Короткий ответ - вы не можете.

Более длинный ответ заключается в том, что foreach является синтаксическим сахаром - он получает итератор из коллекции и называет Next на нем. Это невозможно с двумя коллекциями одновременно.

Если вы просто хотите иметь один цикл, вы можете использовать цикл for и использовать одинаковое значение индекса для обеих коллекций.

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

Альтернативой является объединение обеих коллекций в один, используя оператор LINQ Zip (новый для .NET 4.0) и повторение результата.

Ответ 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(), вероятно, еще проще, но.

Ответ 5

Вместо foreach, почему бы не использовать a for()? например...

int length = list1.length;
for(int i = 0; i < length; i++)
{
    // do stuff with list1[i] and list2[i] here.
}

Ответ 6

Вы можете обернуть два класса IEnumerable < > in 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);
    }
  }
}