Home > other >  How to use Zip<TFirst,TSecond>(IEnumerable<TFirst>, IEnumerable<TSecond>)?
How to use Zip<TFirst,TSecond>(IEnumerable<TFirst>, IEnumerable<TSecond>)?

Time:10-28

Instead having an index inside a for loop, I would like to traverse 2 lists of the same length and have a respective variable assigned to each member of those lists.

https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.zip?view=net-5.0

In this documentation by Microsoft, the first application had an example and I definitely understood how to use it, however, I can't seem to make the second one work and it's what I need for a problem I'm working on.

Here's a sample code to illustrate:

var numbers = new List<int> { 1, 2, 3 };
var words = new List<string> { "one", "two", "three" };
foreach (var (number, word) in Zip(numbers,words))   //this doesn't work
{
    //do something
}

However, the code above throws multiple errors. I tried to manipulate it into looking like the example in microsoft's documention and I got it working. However, it looks too redundant as you can see in the sample code below:

var numbers = new List<int> { 1, 2, 3 };
var words = new List<string> { "one", "two", "three" };
foreach (var (number, word) in numbers.Zip(words, (number,word) => (number,word))) //this lambda expression looks too repetitive
{
    //do something
}

Can anyone help me clean it up?

CodePudding user response:

Enumerable.Zip

Produces a sequence of tuples with elements from the two specified sequences.

You will need to use either

Enumerable.Zip(numbers,words)

or the extension method

numbers.Zip(words)

Also the deconstructed tuple var (first, second) is C# 7.0 or later

Example

var numbers = new List<int> { 1, 2, 3 };
var words = new List<string> { "one", "two", "three" };
foreach (var (first, second) in numbers.Zip(words)) 
   Console.WriteLine($"{first},{second}");

Output

1,one
2,two
3,three

Demo here


If you are using the old and busted .Net Framework or pre .net core 3, then you don't have much of an option other than use the Func parameter. However, you could use a sneakily selector method, delegate, local method, etc

Given

private static (T1, T2) Selector<T1,T2>(T1 t1, T2 t2) => (t1, t2);

Usage

foreach (var (i, s) in numbers.Zip(words,Selector)) 

Or just write your own extension method

public static IEnumerable<(TFirst,TSecond)> Zip<TFirst, TSecond>(
   this IEnumerable<TFirst> first,
   IEnumerable<TSecond> second)
   => first.Zip(second, (first1, second1) => (first1, second1));

CodePudding user response:

So if you want to use it as a method not a extension method first of all you need to use three parameters:

Zip<TFirst,TSecond,TResult>(IEnumerable<TFirst>, IEnumerable<TSecond>, Func<TFirst,TSecond,TResult>)

Thrid one is Func. So basically what you are achieving by second peace of code is:

int[] numbers = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three" };    
Func<int, string, (int, string)> func = (integer, str) => (integer, str);
var numbersAndWords = Enumerable.Zip(numbers, words, func);

foreach (var item in numbersAndWords)
{
    //do smth
    Console.WriteLine(item);
}

.NET core accepts two parameters as you want:

int[] numbers = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three" };
var numbersAndWords = Enumerable.Zip(numbers, words);

foreach (var item in numbersAndWords)
{
    //do smth
    Console.WriteLine(item);
}
  • Related