Reputation: 53
I have a tree structure which is in the following format:
public class MyObject
{
public Guid ID { get; set; }
public Guid ParentID { get; set; }
public string Name { get; set; }
private List<MyObject> MyChildren { get; set; }
}
If I have the ID of deeply nested Child within the children of MyObject I need to traverse upwards and get the Parent > Child relationship in a tree structure using LINQ preferably.
Any Ideas?
Upvotes: 0
Views: 2583
Reputation: 117064
This works for me:
Func<MyObject, IEnumerable<MyObject>> flatten = null;
flatten = mo =>
new [] { mo }
.Concat(mo.MyChildren.SelectMany(x => flatten(x)));
var map = flatten(root).ToDictionary(x => x.ID);
Func<int, IEnumerable<MyObject>> getAncestorPath = null;
getAncestorPath = g =>
map.ContainsKey(g)
? new [] { map[g] }.Concat(getAncestorPath(map[g].ParentID))
: Enumerable.Empty<MyObject>();
To make this work I have to change the List<MyObject> MyChildren { get; set; }
property to public
. If you don't do this we need to know of some other way to get a list of MyObject
so that we don't have to traverse the tree.
So, if I start with this object tree:
var root = new MyObject()
{
ID = 1,
ParentID = 0,
MyChildren = new List<MyObject>()
{
new MyObject()
{
ID = 2,
ParentID = 1,
MyChildren = new List<MyObject>()
{
},
},
new MyObject()
{
ID = 3,
ParentID = 1,
MyChildren = new List<MyObject>()
{
new MyObject()
{
ID = 4,
ParentID = 3,
MyChildren = new List<MyObject>()
{
},
}
},
}
},
};
And I ask for getAncestorPath(4)
then I get this result:
Or, if I show it as a series of ids using String.Join(", ", getAncestorPath(someId).Select(x => x.ID))
then I get this:
4, 3, 1
If you have a list of roots, rather than a single root, then the code changes like so:
var map = roots.SelectMany(root => flatten(root)).ToDictionary(x => x.ID);
Upvotes: 2
Reputation: 1816
if your tree gets rarely mutated, for performance purposes I would "save" two additional fields: a "Left" and a "Right" field. Use the algorithm described here:
http://www.sitepoint.com/hierarchical-data-database-2/
for inspiration. The ideea is simple: You want to get in just one database call all your tree from database and also build it in O ( n) time in memory. If you feel so, you can keep the "ParentID" field and use it for building the in-memory graph. The downside is that rebuilding the tree costs you O ( N ) time (you must recompute the Left and Right values when a node gets inserted / removed or moved in another place in the tree). Otherwise I would use database specific technologies (CTE's)
Example: CTE Recursion to get tree hierarchy
Let's pay attention on performance also!!! it is very important.
Upvotes: 0
Reputation: 10525
private IEnumerable<MyObject> Flatten(IEnumerable<MyObject> objs)
{
return objs.SelectMany(_ => Flatten(_.MyChildren));
}
var childId = 1234;
MyObject treeRoot = ...;
var flatObjects = Flatten(new [] { treeRoot });
var parents = flatObjects.Where(_ => _.MyChildren.Any(child => child.ID == childId));
Upvotes: 0