Whenever you have a parent child relationship and you are not sure how may levels deep it is structured you need to think about recursion.
I'm going to provide a full solution to take a flat normalized collection with a parent/child relationship and use LINQ with recursion to transform into nested collection.
Below is the source for this sample. If you create a new Console Application named RecursionTest and paste this code in the Program.cs file it will run and display the output.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RecursionTest
{
internal class Program
{
private static void Main(string[] args)
{
// Fill list with sample data
var flatObjects = new List
{
new FlatObject(1, 0),
new FlatObject(2, 0),
new FlatObject(3, 1),
new FlatObject(4, 0),
new FlatObject(5, 1),
new FlatObject(6, 2),
new FlatObject(7, 6),
new FlatObject(8, 6)
};
// call the recursive method
var recursiveObjects = FillRecursive(flatObjects, 0);
foreach (var rootNode in recursiveObjects)
{
DumpTreeRootNode(rootNode, 1);
}
Console.WriteLine("Press any key to continue....");
Console.ReadKey();
}
private static void DumpTreeRootNode(RecursiveObject rootNode, int level)
{
if (rootNode != null)
{
WriteFormattedValue(rootNode.Id.ToString(), level);
foreach (var childNode in rootNode.Children)
{
DumpTreeRootNode(childNode, ++level);
}
}
}
private static void WriteFormattedValue(string value, int level)
{
value = value.PadLeft(level, '.');
Console.WriteLine(value);
}
private static List FillRecursive(List flatObjects, int parentId)
{
return
flatObjects.Where(x => x.ParentId.Equals(parentId))
.Select(
item =>
new RecursiveObject
{
Id = item.Id,
ParentId = item.ParentId,
Children = FillRecursive(flatObjects, item.Id)
})
.ToList();
}
}
public class FlatObject
{
public int Id { get; set; }
public int ParentId { get; set; }
public FlatObject(int id, int parentId)
{
Id = id;
ParentId = parentId;
}
}
public class RecursiveObject
{
public int Id { get; set; }
public int ParentId { get; set; }
public List Children { get; set; }
}
}
The first thing we do is set up our flat collection using FlatObject. Create a List of FlatObjects using the constructor to set Id and ParentId. ParentId = 0 in this example is our top level. We have set 3 FlatObjects with this value and they will be at the very top of our nested structure. The other elements are children and their place in our nested structure depends on who their parent is.
Next well call the FillRecursive method with our flat list and our top level (in this case ParentId = 1). We can change the ParentId = 1 and you will only see the element structure where ParentId = 1 is the top level.
Finally within the FillRecursive method we use LINQ to filter the List only grabbing elements for the input ParentId
flatObjects.Where(x => x.ParentId.Equals(parentId))
Then we create a new RecursiveObject List by creating a RecursiveObject for each Flat Object with from above filter using recursion to set the Children collection
Children = FillRecursive(flatObjects, item.Id)