Itertools for C# - Cycle and Zip

Continuing our implementation of Python iterator functions in C#, we'll implement Cycle and Zip this time.

Cycle

IEnumerable Cycle(IEnumerable iterable)

Cycle keeps cycling through the enumerator, that is, it cycles back to the first element when the enumerator is exhausted. You can use it like

IEnumerable<int> source = new List<int>() { 1, 2, 3};
foreach(int element in Itertools.Cycle(source))
{
   Console.WriteLine(element); // prints 1 2 3 1 2 3..
}

The implementation is again very straightforward - all we have to do is wrap the foreach over the iterable in an infinite loop. The outer loop while loop creates a new enumerator for every iteration, as foreach calls GetEnumerator() before looping over the returned enumerator.

public static IEnumerable<T> Cycle<T>(IEnumerable<T> iterable)
{
   while (true)
   {
      foreach (T t in iterable)
      {
          yield return t;
      }
   }
}
The Python cycle function has a slightly different behavior - it caches elements in the iterable the first time around and then iterates over the cache after that. This means that changes to the collection will not be visible to the users of the cycle function, but only if the changes happen after cycle starts returning elements from its cache.

Zip

IEnumerable<T[]> Zip<T>(params IEnumerable<T>[] iterables)

Zip returns an enumerable array of elements, with each array containing one element per input iterator. The first array contains the first element of every iterator, the second array has every second element and so on.

IEnumerable<int> e1 = new List<int>() { 1, 2, 3};
IEnumerable<int> e2 = new List<int>() { 4, 5, 6};
IEnumerable<int[]> zippedValues = Itertools.Zip(e1, e2);
foreach(int[] arr in zippedValues)
{
   Console.WriteLine("{");
   foreach(int val in arr)
       Console.WriteLine(val);
   Console.WriteLine("}");
}

This prints {1,4}, {2,5}, {3,6}

The implementation is slightly more complex.

public static IEnumerable<T[]> Zip<T>(params IEnumerable<T>[] iterables)
{
   IEnumerator<T>[] enumerators = Array.ConvertAll(iterables, (iterable) => iterable.GetEnumerator());

    while (true)
    {
       int index = 0;
       T[] values = new T[enumerators.Length];

       foreach (IEnumerator<T> enumerator in enumerators)
       {
           if (!enumerator.MoveNext())
              yield break;

            values[index++] = enumerator.Current;
       }

       yield return values;
    }
}

The code gets enumerators for all the iterables, moves all enumerators forward, accumulates their current values into an array and yields the array. It does this until any one of the enumerators runs out of elements.

Next time, we'll look at Tee - an interesting function that also proved to be a little tricky to implement.

Comments

# re: Itertools for C# - Cycle and Zip

Thursday, May 01, 2008 9:53 AM by Christopher Grebs

Thanks much for these nice utility-methods.

But it seems that the `Zip` method is not .NET 2.0 compatible (well, or c#2 what ever...)

I get the following error: paste.pocoo.org/.../45985

with that implementation: paste.pocoo.org/.../45986

Is there some bug in your code or is that syntax not supported in c#2?

Since I'm not that experienced in c# it would be nice if you could post a c#2 solution?

with best regards,

Christopher Grebs

# re: Itertools for C# - Cycle and Zip

Monday, May 05, 2008 10:12 AM by Senthil

Yes, C# 2.0 does not have lambda expressions, and the code above uses a lambda expression when calling Array.ConvertAll.

You can use an anonymous method instead - something like

Array.ConvertAll(iterables, delegate(IEnumerable<T> iterable) { return iterable.GetEnumerator(); }));

# re: Itertools for C# - Cycle and Zip

Tuesday, July 29, 2008 5:25 PM by mgsloan

Here's a variation which zips two lists with a function:

       public static IEnumerable<TResult> ZipWith<TSource, TOther, TResult>(this IEnumerable<TSource> a, IEnumerable<TOther> b, Func<TSource, TOther, TResult> f)

       {

           var ae = a.GetEnumerator();

           var be = b.GetEnumerator();

           while (true)

           {

               if (!ae.MoveNext() || !be.MoveNext()) yield break;

               yield return f.Invoke(ae.Current, be.Current);

           }

       }

# re: Itertools for C# - Cycle and Zip

Tuesday, October 28, 2008 3:00 AM by Marc Gravel

Interesting.

Re Zip; first, you should probably be disposing the iterators in a finally block - especially in case of exceptions.

Second; re-using the array is efficient but a bit risky; if I use .ToList() or .ToArray() I'll have "n" references to the *same array*, and no way of obtaining anything except the last row of data. Similarly, chaining to any buffered extension method (sort/reverse/etc) could have very odd effects.

Leave a Comment

(required) 
(required) 
(optional)
(required)