I have list of categories:
╔════╦═════════════╦═════════════╗
║ Id ║ Name ║ Parent_id ║
╠════╬═════════════╬═════════════╣
║ 1 ║ Sports ║ 0 ║
║ 2 ║ Balls ║ 1 ║
║ 3 ║ Shoes ║ 1 ║
║ 4 ║ Electronics ║ 0 ║
║ 5 ║ Cameras ║ 4 ║
║ 6 ║ Lenses ║ 5 ║
║ 7 ║ Tripod ║ 5 ║
║ 8 ║ Computers ║ 4 ║
║ 9 ║ Laptops ║ 8 ║
║ 10 ║ Empty ║ 0 ║
║ -1 ║ Broken ║ 999 ║
╚════╩═════════════╩═════════════╝
Each category have a parent. When parent is 0 - that means it's the root category.
What is the nicest way to convert it to tree structure like below?
Sport
├ Balls
└ Shoes
Electronics
├ Cameras
│ ├ Lenses
│ └ Tripod
│
└ Computers
└ Laptops
Empty
In other words - how to bring data from this structure:
class category
{
public int Id;
public int ParentId;
public string Name;
}
Into this one:
class category
{
public int Id;
public int ParentId;
public string Name;
public List<Category> Subcategories;
}
in universal way? // Universal means not only for mentioned class.
Do you have some smart ideas? ;)
Data:
var categories = new List<category>() {
new category(1, "Sport", 0),
new category(2, "Balls", 1),
new category(3, "Shoes", 1),
new category(4, "Electronics", 0),
new category(5, "Cameras", 4),
new category(6, "Lenses", 5),
new category(7, "Tripod", 5),
new category(8, "Computers", 4),
new category(9, "Laptops", 8),
new category(10, "Empty", 0),
new category(-1, "Broken", 999),
};
If you want to have universal method you''ll need an additional class:
public class TreeItem<T>
{
public T Item { get; set; }
public IEnumerable<TreeItem<T>> Children { get; set; }
}
Then use it with this helper:
internal static class GenericHelpers
{
/// <summary>
/// Generates tree of items from item list
/// </summary>
///
/// <typeparam name="T">Type of item in collection</typeparam>
/// <typeparam name="K">Type of parent_id</typeparam>
///
/// <param name="collection">Collection of items</param>
/// <param name="id_selector">Function extracting item's id</param>
/// <param name="parent_id_selector">Function extracting item's parent_id</param>
/// <param name="root_id">Root element id</param>
///
/// <returns>Tree of items</returns>
public static IEnumerable<TreeItem<T>> GenerateTree<T, K>(
this IEnumerable<T> collection,
Func<T, K> id_selector,
Func<T, K> parent_id_selector,
K root_id = default(K))
{
foreach (var c in collection.Where(c => EqualityComparer<K>.Default.Equals(parent_id_selector(c), root_id)))
{
yield return new TreeItem<T>
{
Item = c,
Children = collection.GenerateTree(id_selector, parent_id_selector, id_selector(c))
};
}
}
}
Usage:
var root = categories.GenerateTree(c => c.Id, c => c.ParentId);
Testing:
static void Test(IEnumerable<TreeItem<category>> categories, int deep = 0)
{
foreach (var c in categories)
{
Console.WriteLine(new String('\t', deep) + c.Item.Name);
Test(c.Children, deep + 1);
}
}
// ...
Test(root);
Output
Sport
Balls
Shoes
Electronics
Cameras
Lenses
Tripod
Computers
Laptops
Empty