Selecting Denormalized Child Nodes and Parents From Tree with Linq

5 August 2009
 

Problem: You have a collection of objects.  Each object has a number of children.  You want all the children matching a criteria, AND the parent.  The child does not reference the parent.

Solution: You can use Linq to

  1. From the context of all the parents, select all the children with the “SelectMany” method.
  2. Construct and select instances of an anonymous type combining the parent and child.  (You can do this because the context is that of the parent.)
  3. Apply your filtering criteria.

Example: Select the five most recently modified children (and parent)

class Program
{
    static void Main()
    {
        var allParents = new StupidFactory().ConstructParents(100);

        var fiveMostRecentChildrenAndParent = allParents
            .SelectMany(parent => parent.Children)
            .Select(child => new { Child = child, Parent = allParents.Where(parent => parent.Children.Contains(child)).Single() })
            .OrderByDescending(anonymousCombo => anonymousCombo.Child.LastModified)
            .Take(5);

        foreach (var childAndParent in fiveMostRecentChildrenAndParent)
        {
            Console.WriteLine("{0} modified on {1} (belongs to {2})", childAndParent.Child.Name, childAndParent.Child.LastModified.ToShortDateString(), childAndParent.Parent.Name);
        }
    } 
}


public abstract class Entity
{
    public string Name;
}

public class Parent : Entity
{
    public List<Child> Children = new List<Child>();
}

public class Child : Entity
{
    public DateTime LastModified;
}

public class StupidFactory
{
    private readonly Random _random = new Random();
    private int _childCount;
    private int _parentCount;

    private Child ConstructChild()
    {
        return new Child { Name = "Child_" + _childCount++, LastModified = DateTime.Now.AddDays(-_random.Next(0, 365)) };
    }

    private Parent ConstructParent()
    {
        var result = new Parent { Name = "Parent_" + _parentCount++ };
        var childCount = _random.Next(0, 11);
        for (var i = 0; i < childCount; i++)
        {
            result.Children.Add(ConstructChild());
        }
        return result;
    }

    public IList<Parent> ConstructParents(int count)
    {
        var result = new List<Parent>();
        for (var i = 0; i < count; i++)
        {
            result.Add(ConstructParent());
        }
        return result;
    }
}

John McDowall

Search

Categories

Archives

Subscribe to Email Updates

Subscribe
 

We are a digital transformation consultancy. We help our clients succeed.

View Services