A more general implementation might be this. Imagine a Node class defined as:
public class Node<TItem, TKey> { public TKey Key { get; set; } public int Level { get; set; } public IEnumerable<TItem> Data { get; set; } public List<Node<TItem, TKey>> Children { get; set; } }
and two common extension methods IEnumerable<T> :
public static List<Node<TItem, TKey>> ToTree<TItem, TKey>(this IEnumerable<TItem> list, params Func<TItem, TKey>[] keySelectors) { return list.ToTree(0, keySelectors); } public static List<Node<TItem, TKey>> ToTree<TItem, TKey>(this IEnumerable<TItem> list, int nestingLevel, params Func<TItem, TKey>[] keySelectors) { Stack<Func<TItem, TKey>> stackSelectors = new Stack<Func<TItem, TKey>>(keySelectors.Reverse()); if (stackSelectors.Any()) { return list .GroupBy(stackSelectors.Pop()) .Select(x => new Node<TItem, TKey>() { Key = x.Key, Level = nestingLevel, Data = x.ToList(), Children = x.ToList().ToTree(nestingLevel + 1, stackSelectors.ToArray()) }) .ToList(); } else { return null; }
You can use these methods to combine a flat list of user objects into a tree with an arbitrary aggregation level and more elegant syntax. The only limitation is that the aggregation keys must be of the same type.
Example:
class A { public int a { get;set; } public int b { get;set; } public int c { get;set; } public int d { get;set; } public string s { get;set; } public A(int _a, int _b, int _c, int _d, string _s) { a = _a; b = _b; c = _c; d = _d; s = _s; } } void Main() { A[] ls = { new A(0,2,1,10,"one"), new A(0,1,1,11,"two"), new A(0,0,2,12,"three"), new A(0,2,2,13,"four"), new A(0,0,3,14,"five"), new A(1,0,3,15,"six"), new A(1,1,4,16,"se7en"), new A(1,0,4,17,"eight"), new A(1,1,5,18,"nine"), new A(1,2,5,19,"dunno") }; var tree = ls.ToTree(x => xa, x => xb, x => xc, x => xd); }
Notes. This implementation is not a βrealβ tree, since there is not a single root node, but you can easily implement the Tree<TItem, TKey> .
NTN