Более общей реализацией может быть эта. Представьте себе класс Node
, определенный как:
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; }
}
и два общих 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;
}
Вы можете использовать эти методы для объединения плоского списка пользовательских объектов в дерево с произвольным уровнем агрегации и более элегантным синтаксисом. Единственное ограничение - ключи агрегации должны быть одного типа.
Пример:
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 => x.a, x => x.b, x => x.c, x => x.d);
}
Примечания. Эта реализация не является "настоящим" деревом, поскольку нет единого корневого узла, но вы можете довольно просто реализовать класс-оболочку Tree<TItem, TKey>
.
НТН