Reputation: 148624
I have an existing class :
public class Product
{
public int Id { get; set; }
public int ParentId { get; set; }
public string Info { get; set; }
}
And I also have a given List<Product>
:
var Lst = new List<Product>();
Lst.Add(new Product{ Id=1,ParentId=0,Info="a"});
Lst.Add(new Product{ Id=2,ParentId=0,Info="a"});
Lst.Add(new Product{ Id=3,ParentId=0,Info="a"});
Lst.Add(new Product{ Id=60,ParentId=1,Info="a"});
Lst.Add(new Product{ Id=61,ParentId=1,Info="a"});
Lst.Add(new Product{ Id=62,ParentId=61,Info="a"});
Lst.Add(new Product{ Id=72,ParentId=61,Info="a"});
Lst.Add(new Product{ Id=90,ParentId=2,Info="a"});
Visualization :
1
|
+---60
|
+---61
|
+---62
|
+---72
2
|
+---90
3
As you can see , the List<>
is flat. (all items are in the same level within the list. it's just that the id,parentId
represents heirarchy)
Now - I need to create structural List<>
so each item in List
will be inside its parent object :
so I created an additional structure class which will hold this structure :
public class Node
{
public Product Product { get; set; }
public List<Node> LstNodes { get; set; }
}
So now I can do :
List<Node> lstNodes = new List<Node>();
And initially I can add the root ones :
lstNodes=Lst.Where(product=>product.ParentId==0).Select(node=>new Node{Product=node}).ToList();
And now I can start recursion to insert items with their parents.
So Where is the problem ?
Question:
I want to avoid insertion of root elements first ( root is where ParentId=0
).
Is there any way of doing this with one recursive method (including roots) ?
Desired result : 3 nodes in lstNodes
where each has its children recursively.
Upvotes: 2
Views: 554
Reputation: 5074
The solutions proposed so far work, but iterate over the entire collection once per node.
This makes them O(n2) and not suitable for large collections (databases, ...).
Here is a solution running in linear time:
var products = new List<Product>();
products.Add(...);
...
var nodes = products.Select(p => new Node { Product = p }).ToList(); // 1
var nodeWithId = nodes.ToDictionary(n => n.Product.Id); // 2
var parentChildren = nodes.Where(n => n.Product.ParentId != 0)
.ToLookup(n => nodeWithId[n.Product.ParentId]); // 3
foreach (var pc in parentChildren) // 4
{
var parent = pc.Key;
var children = pc.ToList();
parent.LstNodes = children;
}
Explanation:
Upvotes: 1
Reputation: 12849
Something like this?
List<Node> GetNodes(List<Product> lst, int parentId = 0)
{
var childProducts = lst.Where(x=>x.ParentId == parentId);
return childProducts
.Select(x=> new Node { Product = x, LstNodes = GetNodes(lst, x.Id)}
.ToList();
}
And purely generic version:
class Node<T>
{
public T Item { get; set; }
public List<Node<T>> LstNodes { get; set; }
}
List<Node<T>> GetNodes<T>(List<T> lst, Func<T, int> idSelector, Func<T, int> parentIdSelector, int parentId = 0)
{
var childProducts = lst.Where(x=>parentIdSelector(x) == parentId);
return childProducts
.Select(x=> new Node<T> { Item = x, LstNodes = GetNodes<T>(lst, idSelector,parentIdSelector, idSelector(x))})
.ToList();
}
GetNodes(Lst,x=>x.Id,x=>x.ParentId, 0);
Upvotes: 4
Reputation: 438
You can try this:
static List<Node> BuildTree(List<Product> lst, int topID)
{
var tree = Enumerable.Empty<Node>().ToList();
if (lst == null)
{
return tree;
}
foreach (var product in lst)
{
if (product.ParentId == topID)
{
Node node = new Node
{
Product = product,
LstNodes = BuildTree(lst.Where(p => p.ParentId == product.Id).ToList(), product.Id)
};
tree.Add(node);
}
}
return tree;
}
And you can call: List<Node> roots = BuildTree(Lst, 0);
Upvotes: 1