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 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.
public static IEnumerable<BlogPost> 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.
if (fn == null)
{
throw new ArgumentNullException("fn", "Supplied function to Collect is not allowed to be null");
}
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.