Skip to main content

Collect - 7 higher order functions

This is my sixth post in a series about higher order functions in C#. Here are the other ones.

Collect

The collect function is mostly used as an intelligent "flatten", as you use it to flatten out data structures. Here's the function signature.

IEnumerable<U> Collect<T, U>(Func<T, IEnumerable<U>> fn, IEnumerable<T> list)

For each item in the list, we produce a list, and in that list every item is yielded. This becomes much clearer with an example.

Examples

var numbers = new[]
{
    new[] { 1, 2, 3 },
    new[] { 4, 5, 6 },
    new[] { 7, 8, 9 },
};

var flat = numbers.Collect(arr => arr); // 1, 2, 3, 4, 5, 6, 7, 8, 9

Almost so simple it's stupid. It just flattens out the array. Let's do something closer to home. Each blog post has comments, and I would like to collect all the comments.

private class BlogPost
{
    public IEnumerable<Comment> Comments { get; private set; }

public static IEnumerable&lt;BlogPost&gt; All()
{
    // return SELECT blogpost.* FROM db
}

}

var blogPosts = BlogPost.All(); var comments = blogPosts.Collect(post => post.Comments);

When you think of it, this is also a "flatten", but with the selected property Comments. Once you understand how to use it, this higher order function is extremely useful.

Implementation

The implementation of Collect is dead simple.

public static IEnumerable<U> Collect<T, U>(Func<T, IEnumerable<U>> fn, IEnumerable<T> list)
{
    if (list == null)
    {
        throw new ArgumentNullException("list", "Supplied list to Collect is not allowed to be null");
    }

if (fn == null)
{
    throw new ArgumentNullException(&quot;fn&quot;, &quot;Supplied function to Collect is not allowed to be null&quot;);
}

foreach (var item1 in list)
foreach (var item2 in fn(item1))
{
    yield return item2;
}

}

public static IEnumerable<U> Collect<T, U>(this IEnumerable<T> list, Func<T, IEnumerable<U>> fn) { return Collect(fn, list); }

This loops through the list, selecting the property that should be collecting, and yielding all items in those child lists. Pretty simple.

Conclusion

We have taken a look on collect and how to use it to flatten, not only multi leveled collections, but also object graphs. This becomes very useful in situations where the value you have is a 1..N relation with the value you really want.

comments powered by Disqus