views:

617

answers:

5

In Python there is a really neat function called zip which can be used to iterate through two lists at the same time:

list1 = [1, 2, 3]
list2 = ["a", "b", "c"]
for v1, v2 in zip(list1, list2):
    print v1 + " " + v2

The above code shoul produce the following:

1 a
2 b
3 c

I wonder if there is a method like it available in .Net? I'm thinking about writing it myself, but there is no point if it's already available.

+4  A: 

Nope, there is no such function in .NET. You have roll out your own. Note that C# doesn't support tuples, so python-like syntax sugar is missing too.

You can use something like this:

class Pair<T1, T2>
{
    public T1 First { get; set;}
    public T2 Second { get; set;}
}

static IEnumerable<Pair<T1, T2>> Zip<T1, T2>(IEnumerable<T1> first, IEnumerable<T2> second)
{
    if (first.Count() != second.Count())
        throw new ArgumentException("Blah blah");

    using (IEnumerator<T1> e1 = first.GetEnumerator())
    using (IEnumerator<T2> e2 = second.GetEnumerator())
    {
        while (e1.MoveNext() && e2.MoveNext())
        {
            yield return new Pair<T1, T2>() {First = e1.Current, Second = e2.Current};
        }
    }
}

...

var ints = new int[] {1, 2, 3};
var strings = new string[] {"A", "B", "C"};

foreach (var pair in Zip(ints, strings))
{
    Console.WriteLine(pair.First + ":" + pair.Second);
}
aku
+1 for using using-blocks
Lasse V. Karlsen
+16  A: 

Update: It will be built-in in C# 4!

Here is a C# 3 version:

IEnumerable<TResult> Zip<TResult,T1,T2>
    (IEnumerable<T1> a,
     IEnumerable<T2> b,
     Func<T1,T2,TResult> combine)
{
    using (var f = a.GetEnumerator())
    using (var s = b.GetEnumerator())
    {
        while (f.MoveNext() && s.MoveNext())
            yield return combine(f.Current, s.Current);
    }
}

Dropped the C# 2 version as it was showing its age.

Matt Howells
Would be nice to define Pair<,> as well or take an additional parameter that serves as the result selector.
Atif Aziz
Wow, I'm amazed. While I was typing my answer you provide almost 100% similar solution :)
aku
This is one of the ideal situations for using the "var" keyword (if you were on 3.5): foreach (var pair in Zip(ints, strings))You lose the noise of the Pair<int, string> declaration which for me is unneeded.
Garry Shutler
Garry: I agree, but C# 2.0 is still in much wider use so I thought I would stick to that for this example.
Matt Howells
+7  A: 

As far as I know there is not. I wrote one for myself (as well as a few other useful extensions and put them in a project called NExtension on Codeplex.

Apparently the Parallel extensions for .NET have a Zip function.

Here's a simplified version from NExtension (but please check it out for more useful extension methods):

public static IEnumerable<TResult> Zip<T1, T2, TResult>(this IEnumerable<T1> source1, IEnumerable<T2> source2, Func<T1, T2, TResult> combine)
{
    using (IEnumerator<T1> data1 = source1.GetEnumerator())
    using (IEnumerator<T2> data2 = source2.GetEnumerator())
        while (data1.MoveNext() && data2.MoveNext())
        {
            yield return combine(data1.Current, data2.Current);
        }
}

Usage:

int[] list1 = new int[] {1, 2, 3};
string[] list2 = new string[] {"a", "b", "c"};

foreach (var result in list1.Zip(list2, (i, s) => i.ToString() + " " + s))
    Console.WriteLine(result);
Cameron MacFarland
Nice answer. You should add an example of how to use the function in C# 3.0
Coincoin
same comment here as the accepted answer, IEnumerator<T> is IDisposable, you should add some using-blocks.
Lasse V. Karlsen
+1  A: 

There's also one in F#:

let zipped = Seq.zip firstEnumeration secondEnumation

TraumaPony
A: 

That's a really cool function. I've never seen it before (I've never worked with Python or F#). Out of curiousity, what happens if the two lists are different lengths?

unforgiven3
Interesting point. I wrote a small test program in Python to check and it stops looping the moment one of the lists ends. This could of course be changed in and implementation if wanted.Python has some intersting features like this one. If interested check out http://docs.python.org/tut/node7.html
Jonas
In all implementations I've seen in functional languages, the result is as long as the shortest list.
Daniel