C# Coding; Missing Functions on IEnumerable
January 3rd, 2008
Me old mucker Spencer pointed out today that C# 3’s newly-refurbished IEnumerable<T> class lacks some basic features. Specifically, it lacks equivalents for the classic Map, Filter, and Reduce functions seen in functional languages. The first two are familiar as List<T>.ConvertAll, and List<T>.FindAll. The third isn’t so familiar, but is still very useful. I’ve also thrown in an implementation of ForEach for free.
Ben Hall points out that it’s possible to extend the class, but I wanted to get a full, commented implementation of the three functions. Feel free to use this code in your own work.
So, here they are;
IEnumerableExtras
public static class IEnumerableExtras
{
/// <summary>
/// Do 'action' to every item in the list.
/// </summary>
/// <typeparam name="T">The source type</typeparam>
/// <param name="list">the IEnum</param>
/// <param name="action">the action to perform.</param>
public static void ForEach<T>
(this IEnumerable<T> list, Action<T> action)
{
foreach (T item in list) { action(item); }
}
/// <summary>
/// Convert every item in the list using the converter
/// function
/// </summary>
/// <typeparam name="T">The source type</typeparam>
/// <typeparam name="U">The destination type</typeparam>
/// <param name="list">the list to convert</param>
/// <param name="converter">a function to convert
/// one item to another.</param>
/// <returns>all items in the list converted by
/// the converter function.</returns>
public static IEnumerable<U> Map<T, U>
(this IEnumerable<T> list, Converter<T, U> converter)
{
foreach (T item in list)
{
yield return converter(item);
}
}
/// <summary>
/// Returns a new enumerator containing only those
/// elements which return true from 'condition'.
/// </summary>
/// <typeparam name="T">The source type</typeparam>
/// <param name="list">the list to filter</param>
/// <param name="condition">the 'keep in' condition</param>
/// <returns>the items for which condition(item)
/// is true</returns>
public static IEnumerable<T> Filter<T>
(this IEnumerable<T> list, Predicate<T> condition)
{
foreach (T item in list)
{
if (condition(item))
{
yield return item;
}
}
}
/// <summary>
/// Reduces a list of items to a single item; can be
/// used to, say, sum a list of integers, or
/// concatenate a number of strings, or find the
/// maximum value in a collection.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <param name="reducer"></param>
/// <returns></returns>
public static T Reduce<T>
(this IEnumerable<T> list, Func<T, T, T> reducer)
{
IEnumerator<T> enumerator = list.GetEnumerator();
if (enumerator.MoveNext())
{
// we have some items; start combining them together.
T aggregator = enumerator.Current;
while (enumerator.MoveNext())
{
aggregator = reducer(aggregator,
enumerator.Current);
}
return aggregator;
}
else
{
// there was nothing in the list; return default.
return default(T);
}
}
}
And here’s an example program;
static void Main(string[] args)
{
IEnumerable<double?> maybeDoubles =
new List<double?> {1, 2, null, 3, 4, null, null, null};
// remove all the empty values: <double?>[1,2,3,4]
IEnumerable<double?> noNulls = maybeDoubles.Filter(x => x.HasValue);
// convert Nullable to non-nullable: ><double>[1,2,3,4]
IEnumerable<double> notNullable = noNulls.Map(x => x.Value);
// convert to strings so we can display them. <string>["1", "2", "3", "4"]
IEnumerable<string> stringVersions = notNullable.Map(x=>x.ToString());
// join the strings together with commas “1, 2, 3, 4″
string displayString = stringVersions.Reduce( (s1, s2) => s1 + “, ” + s2);
// show us the result;
Console.WriteLine(displayString);
Console.ReadLine();
}
So there you go. Enjoy.











