Iterate Two Lists or Arrays with One Foreach Statement in C#

Iterate two Lists or Arrays with one ForEach statement in C#

This is known as a Zip operation and will be supported in .NET 4.

With that, you would be able to write something like:

var numbers = new [] { 1, 2, 3, 4 };
var words = new [] { "one", "two", "three", "four" };

var numbersAndWords = numbers.Zip(words, (n, w) => new { Number = n, Word = w });
foreach(var nw in numbersAndWords)
{
Console.WriteLine(nw.Number + nw.Word);
}

As an alternative to the anonymous type with the named fields, you can also save on braces by using a Tuple and its static Tuple.Create helper:

foreach (var nw in numbers.Zip(words, Tuple.Create)) 
{
Console.WriteLine(nw.Item1 + nw.Item2);
}

Using foreach loop to iterate through two lists


foreach(object o in a.Concat(b)) {
o.DoSomething();
}

Iterate two Arrays with ForEach statement in C#

You could simply use a for-loop:

for(int i = 0; i < NamesValues.Length; i++)
{
string name = NamesValues[i];
string totalValue = TotalValues[i];
}

If the length is different you could use ElementAtOrDefault:

for(int i = 0; i < Math.Max(NamesValues.Length, TotalValues.Length) ; i++)
{
string name = NamesValues.ElementAtOrDefault(i);
string totalValue = TotalValues.ElementAtOrDefault(i);
}

You also could use Enumerable.Zip to create an anonymous type with both values:

var namesAndValues = NamesValues.Zip(TotalValues, 
(n, tv) => new { Name = n, TotalValue = tv });
foreach(var nv in namesAndValues)
{
Console.WriteLine("Name: {0} Value: {1}", nv.Name + nv.TotalValue);
}

Note that the second approach using a for-loop and ElementAtOrDefault is different to to the Enumerable.Zip approach.

  • The former iterates both arrays completely and ElementAtOrDefault returns null for the value of the smaller array if there is no element at that index.
  • The latter Zip combines elements only until it reaches the end of one of the sequences.

So when you use Math.Min instead of Math.Max in the for-loop you get a similar behaviour, but then you should use the first approach anyway since there's no need for ElementAtOrDefault.

using foreach to iterate simultaneously through multiple lists (syntax sugar)

You can do what foreach does under the hood, but with two enumerators:

using(var e1 = list1.GetEnumerator())
using(var e2 = list2.GetEnumerator())
{
while(e1.MoveNext() && e2.MoveNext())
{
var item1 = e1.Current;
var item2 = e2.Current;

// use item1 and item2
}
}

For convenience, you can write an extension method like the following that takes an action:

public static void ZipDo<T1, T2>( this IEnumerable<T1> first, IEnumerable<T2> second, Action<T1, T2> action)
{
using (var e1 = first.GetEnumerator())
using (var e2 = second.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext())
{
action(e1.Current, e2.Current);
}
}
}

and use it like:

list1.ZipDo(list2, (i1,i2) => i1.Use(i2));

By the way, you can expand this to use 3 or more lists:

public static void ZipDo<T1, T2, T3>(this IEnumerable<T1> first,
IEnumerable<T2> second, IEnumerable<T3> third,
Action<T1, T2, T3> action)
{
using (var e1 = first.GetEnumerator())
using (var e2 = second.GetEnumerator())
using (var e3 = third.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
{
action(e1.Current, e2.Current, e3.Current);
}
}
}

The approach above is required when the collections have different generic types. However, if they all have the same generic type, then you can write a flexible method that takes any number of IEnumerable<T>s:

public static void ZipAll<T>(this IEnumerable<IEnumerable<T>> all, Action<IEnumerable<T>> action)
{
var enumerators = all.Select(e => e.GetEnumerator()).ToList();
try
{
while (enumerators.All(e => e.MoveNext()))
action(enumerators.Select(e => e.Current));
}
finally
{
foreach (var e in enumerators)
e.Dispose();
}
}

and use it:

var lists = new[] {
new[]{ 1, 1, 1 },
new[]{ 2, 2, 2 },
new[]{ 3, 3, 3 }};

lists.ZipAll(nums => Console.WriteLine(nums.Sum()));
// 6
// 6
// 6

How to iterate through two collections of the same length using a single foreach

Edit - Iterating whilst positioning at the same index in both collections

If the requirement is to move through both collections in a 'synchronized' fashion, i.e. to use the 1st element of the first collection with the 1st element of the second collection, then 2nd with 2nd, and so on, without needing to perform any side effecting code, then see @sll's answer and use .Zip() to project out pairs of elements at the same index, until one of the collections runs out of elements.

More Generally

Instead of the foreach, you can access the IEnumerator from the IEnumerable of both collections using the GetEnumerator() method and then call MoveNext() on the collection when you need to move on to the next element in that collection. This technique is common when processing two or more ordered streams, without needing to materialize the streams.

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
}

As others have pointed out, if the collections are materialized and support indexing, such as an ICollection interface, you can also use the subscript [] operator, although this feels rather clumsy nowadays:

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

Finally, there is also an overload of Linq's .Select() which provides the index ordinal of the element returned, which could also be useful.

e.g. the below will pair up all elements of collection1 alternatively with the first two elements of collection2:

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

FOREach loop for two ArrayList in same time c#

If you necessarily want to Use Foreach, you have to Zip the two collections first.

 foreach (var (username, password) in username_list.Zip(password_list, (x, y) => (x, y))
Console.WriteLine($"{user} {password}");

Zipping has nothing todo with Zip-Files here, just with a regular zipper (take one from the left, one from the right, and join them).

How to use two arrays in one foreach loop using C#


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

var numbersAndWords = numbers.Zip(words, (first, second) => first + " " + second);

foreach (var item in numbersAndWords)
Console.WriteLine(item);

// This code produces the following output:

// 1 one
// 2 two
// 3 three

iterate multiple lists with foreach in c#

Yet another strange solution in fluent style with .Zip() operator:

var names = new[] { "matin", "mobin", "asghar" };
var families = new[] { "shomaxe", "jjj", "reyhane" };
var numbers = new[] { 1, 2, 3 };
var ages = new[] { 21, 23, 24 };

names
.Zip(families, (name, family) => $"{name} {family}")
.Zip(numbers, (res, number) => $"{res} {number}")
.Zip(ages, (res, age) => $"{res} {age}")
.ToList()
.ForEach(x => Console.WriteLine(x));

From the docs about Zip operator.

Looping through 2 lists at once


Modern Answer

LINQ now has a built-in Zip method, so you don't need to create your own. The resulting sequence is as long as the shortest input. Zip currently (as of .NET Core 3.0) has 2 overloads. The simpler one returns a sequence of tuples. It lets us produce some very terse code that's close to the original request:

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

foreach (var (number, word) in numbers.Zip(words))
Console.WriteLine($"{number}, {word}");

// 1, one
// 2, two
// 3, three

Or, the same thing but without tuple unpacking:

foreach (var item in numbers.Zip(words))
Console.WriteLine($"{item.First}, {item.Second}");

The other overload (which appeared earlier than Core 3.0) takes a mapping function, giving you more control over the result. This example returns a sequence of strings, but you could return a sequence of whatever (e.g. some custom class).

var numbersAndWords = numbers.Zip(words, (number, word) => $"{number}, {word}");

foreach (string item in numbersAndWords)
Console.WriteLine(item);

If you are using LINQ on regular objects (as opposed to using it to generate SQL), you can also check out MoreLINQ, which provides several zipping methods. Each of those methods takes up to 4 input sequences, not just 2:

  • EquiZip - An exception is thrown if the input sequences are of different lengths.

  • ZipLongest - The resulting sequence will always be as long as the longest of input sequences where the default value of each of the shorter sequence element types is used for padding.

  • ZipShortest - The resulting sequence is as short as the shortest input sequence.

See their examples and/or tests for usage. It seems MoreLINQ's zipping came before regular LINQ's, so if you're stuck with an old version of .NET, it might be a good option.



Related Topics



Leave a reply



Submit