5

I am looking for an elegant solution to aggregate a child collection in a collection into one large collection. My issue is when certain child collections could be null.

EG:

var aggregatedChildCollection = parentCollection.SelectMany(x=> x.ChildCollection);

This throws an exception should any of the child collection objects be null. Some alternatives are:

// option 1
var aggregatedChildCollection = parentCollection
    .Where(x=>x.ChildCollection != null)
    .SelectMany(x => x.ChildCollection);

// option 2
var aggregatedChildCollection = parentCollection
    .SelectMany(x => x.ChildCollection ?? new TypeOfChildCollection[0]);

Both would work but I am doing a certain operation on quite a few child collections on the parent, and it is becoming a bit unweilding.

What I would like is to create an extension method that checks if the collection is null and if so does what option 2 does - adds an empty array. But my understanding of Func is not to a point where I know how to code this extension method. I do know that the syntax I would like is like this:

var aggregatedChildCollection = parentCollection.SelectManyIgnoringNull(x => x.ChildCollection);

Is there a simple extension method that would accomplish this?

DenverCoder9
  • 1,119
  • 3
  • 16
  • 30

4 Answers4

4

You can use a custom extension method:

public static IEnumerable<TResult> SelectManyIgnoringNull<TSource, TResult>(
    this IEnumerable<TSource> source, 
    Func<TSource, IEnumerable<TResult>> selector)
{
    return source.Select(selector)
        .Where(e => e != null)
        .SelectMany(e => e);
}

And use like this:

var aggregatedChildCollection = parentCollection
    .SelectManyIgnoringNull(x => x.ChildCollection);
DavidG
  • 113,891
  • 12
  • 217
  • 223
  • A great solution - in my case I have combined it with Cory Nelson's answer that uses `Enumerable.Empty()` instead of cutting out the null collections so that I will always have at the very least an empty collection. – DenverCoder9 Sep 06 '16 at 00:45
  • That also works, but be mindful that Cory linked to code that has a licence attached, so you will need to provide attribution. – DavidG Sep 06 '16 at 00:46
1

If ParentCollection is your own class you should also be able to a default constructor to the class such as:

public ParentCollection{
    public ParentCollection() {
        ChildCollection = new List<ChildCollection>();
    }
}

That should prevent the null ref exception and give you an empty list if it doesn't have anything in it. At least this works with EF models.

Eonasdan
  • 7,563
  • 8
  • 55
  • 82
  • 2
    No, unfortunately I cannot guarantee that any of the child collections will be populated. It's not that the class is invalid if a child collection is null, I just want to group all the child collections into one collection and ignore any nulls by treating them as an empty collection. – DenverCoder9 Sep 06 '16 at 00:12
1

Your "option 2" is what I would do, with a minor tweak: use Enumerable.Empty() instead of creating an empty array to reduce the amount of new objects you're creating.

I use a trivial extension method Touch() -- named after the *nix utility -- to keep the LINQ syntax flow and reduce typing:

public static IEnumerable<T> Touch<T>(this IEnumerable<T> items) =>
    items ?? Enumerable.Empty<T>();

And would use it as:

var aggregatedChildCollection = parentCollection
    .SelectMany(x => x.ChildCollection.Touch());
Cory Nelson
  • 29,236
  • 5
  • 72
  • 110
1

You can avoid the overhead from the LINQ extension by using the SelectMany Reference Source

public static IEnumerable<TResult> SelectManyNotNull<TSource, TResult>(
         this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector) 
{
    foreach (TSource element in source)
    {
        var subElements = selector(element);
        if (subElements != null)
            foreach (TResult subElement in subElements )
                yield return subElement;
    }
}
Slai
  • 22,144
  • 5
  • 45
  • 53