some1pretty
some1pretty

Reputation: 13

Group list of objects with corresponding child with multiple inheritance using LINQ

I'll summarize the problem using simpler objects. I also belive this is not a duplicate problem.

I have a flat IEnumerable of Type1 that needs to be converted into an IEnumerable of Type2 where each object contains an IEnumerable of its children. I'm using AutoMapper so mapping from Type1 to Type2 is not a problem. The problem is each children may have children of its own and those grandchildren may have its own children.

public class Type1
{
    public int? ParentId { get; set; }
    public int Id { get; set; }
    ...other properties
}

public class Type2
{
    public int Id { get; set; }
    public IEnumerable<Type2> Children { get; set; }
    ...other properties
}



IEnumerable<Type1> source = GetItems();
IEnumerable<Type2> result = ...someLinqExpression....;

Is it possible to "un-flat" a list and group the children using linq? Or is there an alternative to using linq with good performance and readibility?

This is what I have so far:

        var parents = source
            .Where(element => element.ParentId == null);

        source = source.Except(parents);

        parents
            .Select(element => Mapper.Map<Type2>(element))
            .ToList()
            .ForEach(parent =>
                parent.Children = source
                .Where(child => child.ParentId == parent.Id)
                .Select(child => Mapper.Map<Type2>(child)));

Upvotes: 1

Views: 260

Answers (1)

Jamiec
Jamiec

Reputation: 136074

You should be able to do this by writing a recursive function, something along the lines of

public IEnumerable<Type2> RecursiveBuildHierarchy(IEnumerable<Type1> parents, IEnumerable<Type1> entireList)
{
    var type2Parents = parents.Select(element => Mapper.Map<Type2>(element));
    foreach(var parent in type2Parents)
    {
        parent.Children = RecursiveBuildHierarchy(entireList.Where(x => x.ParentId = parent.Id), entireList);
    }
    return type2Parents;
}

And you would start off with:

IEnumerable<Type1> source = GetItems();
IEnumerable<Type2> result = RecursiveBuildHierarchy(source.Where(x => x.ParentId == null), source);

There is probably some nifty way to get this to work directly with your mapping library too! I personally find a bit of a "code smell" with this idea of mutating a property after mapping.

Upvotes: 1

Related Questions