Converting IEnumerable<T?> to IEnumerable<T>
This morning on the forum there was a question regarding nullable types (ie decimal?) and how to convert an IEnumerable collection containing nullable types to one containing the equivalent non-nullable type (decimal). This raised an interesting question, how do you convert a collection containing one type of object, into a collection containing another type of object, be it nullable>nonnullable or otherwise.
To start with, I looked at the extension methods of IEnumerable<T>, and in particularly Cast<>. However, if the value is null then we are going to have to handle it differently which cast doesn't have the ability to do. So no luck there and there didn't seem any other methods.
I then looked at the methods which List<> contains, this implements IEnumerable<> so its part of the same type. List<> does have a method which allows for this requirement, called ConvertAll() which takes a Converter object, which in turn takes a method name which is called for each object in the list to handle the conversion. This then allowed me to write this code:
List<decimal> nonNull;
List<decimal?> nullable = new List<decimal?>();
nullable.Add(null);
nullable.Add(1);
nonNull = nullable.ConvertAll(new Converter<decimal?, decimal>(ConvertToNonNull));
foreach (var i in nonNull)
{
Console.WriteLine(i);
}
public static decimal ConvertToNonNull(decimal? nullValue)
{
return nullValue.GetValueOrDefault(0);
}
ConvertToNonNull is called for every item in the list, taking advantage of generics to keep everything strongly typed. The GetValueOrDefault() is available to use on all nullable types, you simple give it the value as a parameter you want it to return if the value is null.
The problem is that you have to be working in terms of List<> and not IEnumerable
public static class IEnumerableExtension
{
public static IEnumerable<TOutput> ConvertAll<T, TOutput>(this IEnumerable<T> collection, Converter<T, TOutput> converter)
{
if (converter == null)
throw new ArgumentNullException("converter");
List<TOutput> list = new List<TOutput>();
foreach (T i in collection)
{
list.Add(converter(i));
}
return list;
}
}
The extension method above attaches itself to IEnumerable<> and takes a Converter as a parameter - just the same as List does. I then check to make sure its not null, create a new internal list of the outputting type (need a way to hold the converted items internally). Then for each item in the source collection, I call the converter method. Finally I return the list as a return type of IEnumerable<>. Here's the calling code.
IEnumerable<decimal> notNull;
IEnumerable<decimal?> INull;
INull = nullable; //Original list.
notNull = INull.ConvertAll(new Converter<decimal?, decimal>(ConvertToNonNull)); //My new extension method
foreach (decimal i in notNull)
{
Console.WriteLine(i);
}
One of the nice things about extension methods is that if a method, with the same name and parameter list, is defined locally within the class then it overrides the extension implementation. So, the List<>.ConvertAll() functionality is not affected.
Hope you find a use for this.
Labels: Linq, Visual Studio 2008





Blogger comments
foreach (T value in collection)
{
yield return (value == null)
? default(TOutput)
: (TOutput) Convert.ChangeType(value, typeof(TOutput));
}
Then you could just use:
notNulls = nulls.ConvertAll();
Does that work? (I don't feel like firing up the VM now... :)
Great comment! Including code like below, will create a overload so you can decide to use a converter or just a basic type converstion.
(Had to remove < as blogger says that tag is not allowed).
public static IEnumerable TOutput ConvertAll T, TOutput (this IEnumerable T collection)
{
foreach (T value in collection)
{
yield return (value == null)
? default(TOutput)
: (TOutput)Convert.ChangeType(value, typeof(TOutput));
}
}
However, using a Converter is really useful when doing conversation other than type, maybe pulling data from an additional source. By having it as an overload we get the best of both worlds!
There is a bit of a problem when calling it. Because it uses two generic types, you have to actually define what they are as it can only infer the base type of the collection.
You would need to call it like this:
notNull = INull.ConvertAll decimal?, decimal ();
Apart from that, neat idea!
It's unfortunate about having to specify the generic types though. On well, at least it worked :)
BTW, I've been following your posts on 3.5 features and I'm learning some cool stuff.
Thanks and keep up the good work.
If we only want to add a extension method to convert a list of nullable type into its equivalent non-nullable type, we may just need to add an extension method to the IEnumerable<Nullable<T>>:
public static IEnumerable<T> ConvertToNonNullable<T>(this IEnumerable<Nullable<T>> target) where T : struct
{
foreach (Nullable<T> item in target)
{
yield return item.GetValueOrDefault();
}
}
I'd suggest that if you have a nullable list, you probably want to strip out the null values, not convert them to zero; null probably indicates 'the value is not known', not 'the value is zero'. You can do this by combining a filter and a convert;
IEnumerable<double> cleanlist = collection.Filter(x => x.HasValue).Map(x => x.Value);