Bookmark and Share

Dyadic Map and Higher in .NET 4

.NET has included monadic map functionality since the release of LINQ in .NET 3.5. You may know this by the extension method on IEnumerable<T>, Select. This allows you to project from one sequence to another. However, that’s as far as it went. Applying a map on multiple lists was not available in the core framework. That has changed in .NET 4 with the new Zip method.

Zip gives you the ability to map two sequences into one sequence; in effect, zipping it up. Here’s an example:

var ids = new[] { 1, 2, 3 };
var firstNames = new[] { "George", "John", "Thomas" };
var presidents = ids.Zip(firstNames, (i, f) => Tuple.Create(i, f));

zipperI first created an array of numbers, then an array of strings. Then, I zipped it up to create pairs of each one. Here’s the output when the pairs are written to the console:

(1, George)
(2, John)
(3, Thomas)

One thing I find disappointing is that there are no overloads provided for Zip. What if I need to map more than two sequences? It’s fairly trivial to implement, and I believe it should have been included in the .NET framework. For now, we will have to settle with creating our own.

public static class Enumberable
{
    public static IEnumerable<TResult> Zip<TFirst, TSecond, TThird, TResult>
        (this IEnumerable<TFirst> first, IEnumerable<TSecond> second, 
            IEnumerable<TThird> third, Func<TFirst, TSecond, TThird, TResult> selector)
    {
        if (first == null)
        {
            throw new ArgumentNullException("first");
        }
        if (second == null)
        {
            throw new ArgumentNullException("second");
        }
        if (third == null)
        {
            throw new ArgumentNullException("third");
        }
        if (selector == null)
        {
            throw new ArgumentNullException("selector");
        }

        using (var firstEnumerator = first.GetEnumerator())
        using (var secondEnumerator = second.GetEnumerator())
        using (var thirdEnumerator = third.GetEnumerator())
        {

            while (firstEnumerator.MoveNext() && secondEnumerator.MoveNext()
                && thirdEnumerator.MoveNext())
            {
                yield return selector(firstEnumerator.Current, secondEnumerator.Current,
                    thirdEnumerator.Current);
            }
        }
    }
}

This creates a Zip extension utilizing three sequences. If you need more, just copy the code and add appropriate code for appropriate arguments. **EDIT** I forgot to add using clauses.

With this version, I will create an anonymous type to name the properties instead of using a tuple.

var ids = new[] { 1, 2, 3 };
var firstNames = new[] { "George", "John", "Thomas" };
var lastNames = new[] { "Washington", "Adams", "Jefferson" };
var presidents = ids.Zip(firstNames, lastNames,
    (i, f, l) => new { Id = i, FirstName = f, LastName = l });

Here is the output when the presidents are written to the console.

{ Id = 1, FirstName = George, LastName = Washington }
{ Id = 2, FirstName = John, LastName = Adams }
{ Id = 3, FirstName = Thomas, LastName = Jefferson }

As with Select, you are not limited to anonymous types or tuples. This functionality is useful when reading data from disparate sources to create sequences of objects.

blog comments powered by Disqus

KodefuGuru.GetInfo()

Chris Eargle
LinkedIn Twitter Technorati Facebook

Chris Eargle
Telerik Developer Evangelist, C# MVP

JustCode

Telerik .NET Ninja

 

INETA Community Speakers Program

 

MVP - Visual C#

 

Friend of RedGate

World Map

Tag cloud

Month List

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer’s view in any way.