Many a time I've had to build a relationship where a parent object has a list of child objects linked to it, where the parent itself also belongs to a list. There are many ways of doing this, however, the most performant way I've found is to use an extension on the Enumerable class;
ToLookup.
Lets say we have a list of parent objects and a list of child objects, and that we need to link to each parent all its children.
Parent Object:
public class Parent
{
public long Identifier { get; set; }
public List Children { get; set; } = new List();
public Parent(long identifier)
{
this.Identifier = identifier;
}
}
Child Object:
public class Child
{
public long Identifier { get; set; }
public long ParentIdentifier { get; set; }
public Child(long identifier, long parentIdentifier)
{
this.Identifier = identifier;
this.ParentIdentifier = parentIdentifier;
}
}
We will assume we have some code that gets us a large list of parents and a large list of children to link to the parents. Each parent has an average of 25 children, some could have no children, some could have more than 25.
List parents = GetParents().ToList();
List children = GetChildren().ToList();
Typically, I would loop through the parents and do a search of the children for any that have a corresponding parent id.
foreach (var parent in parents)
{
parent.Children = children.Where(d => d.ParentIdentifier == parent.Identifier).ToList();
}
While this method of linking the children to the parents is straightforward, and works fine for small data sets, it really hits the wall on a large dataset; such as our example. The above method takes an average of 88 seconds to complete the linking. We can do better.
Lets use the Enumerable.ToLookup instead. We will use ToLookup to build a dictionary of the children grouped by the parent. Then its a simple retrieval of the children for a given parent.
var childrenLookup = children.ToLookup(d => d.ParentIdentifier);
foreach (var parent in parents)
{
parent.Children = childrenLookup[parent.Identifier].ToList();
}
Using the ToLookup knocks our average linking time down to 0.08 seconds! That's a 99% performance increase!