Saturday, February 12, 2011

Is there a linq-y way to union collection properties of a collection of objects?

Sorry, that title just hurts. I'm wondering if there is a Linq to collections extension method that collapses the following code segment into a single line:

public IEnumerable<Child> GetAllChildren(IEnumerable<Parent> parents){

  var result = new List<Child>();

  foreach(Parent parent in parents)
    foreach(Child child in parent.Children)
      result.Add(child);

  return result;
}

If you can collapse that into a single statement, try it on insane difficulty:

public IEnumerable<Child> GetAllChildren(IEnumerable<Grandparent> nanas){

  var result = new List<Child>();

  foreach(Grandparent papa in nanas)
    foreach(Parent parent in papa.Children)
      foreach(Child child in parent.Children)
        result.Add(child);

  return result;
}
  • This will work:

    public IEnumerable<Child> GetAllChildren(IEnumerable<Parent> parents)
    {
        return from parent in parents
               from child in parent.Children
               select child;
    }
    

    and then this:

    public IEnumerable<Child> GetAllChildren(IEnumerable<Grandparent> nanas)
    {
        return from papa in nanas
               from parent in papa.Children
               from child in parent.Children
               select child;
    }
    

    Note, in this example I'm not actually returning a list, I'm returning an IEnumerable data source that until you start to foreach over it, or similar, won't actually do any processing.

    If you need to return a list, modify each return statement as follows:

        return (from .....
                ...
                select child).ToList();
    
    Will : The return value has to be an IEnumerable, and the easiest way (at least as far as I know) to do that is return a collection that implements IEnumerable. Thanks for the answer!
    Lasse V. Karlsen : You can return a collection, or use yield return, look up the yield keyword on msdn if you're not familiar with it.
    Will : Good point on the yield... I usually don't do that in these types of cases due to a step along the way failing, in which case I just return the empty collection. Its pretty obvious what I'm doing, whereas a yield sometimes isn't clear what happens in these cases for people who come behind me...
  • Here's the obligatory method form.

    return parents
      .SelectMany(p => p.Children);
    

    And for two levels:

    return oldies
      .SelectMany(grand => grand.Children)
      .SelectMany(parent => parent.Children);
    
    From David B

0 comments:

Post a Comment