Reputation: 13
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
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