我有一个树形结构,格式如下:
public class MyObject
{
public Guid ID { get; set; }
public Guid ParentID { get; set; }
public string Name { get; set; }
private List<MyObject> MyChildren { get; set; }
}
如果我在 MyObject 的子项中有深层嵌套Child的 ID ,我需要向上遍历并获取Parent&gt;最好使用LINQ的树结构中的子关系。
任何想法?
答案 0 :(得分:2)
这对我有用:
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>();
为了完成这项工作,我必须将List<MyObject> MyChildren { get; set; }
属性更改为public
。如果您不这样做,我们需要了解其他一些获取MyObject
列表的方法,以便我们不必遍历该树。
所以,如果我从这个对象树开始:
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>()
{
},
}
},
}
},
};
我要求getAncestorPath(4)
然后我得到这个结果:
或者,如果我使用String.Join(", ", getAncestorPath(someId).Select(x => x.ID))
将其显示为一系列ID,那么我就明白了:
4,3,1
如果你有一个根列表而不是一个根,那么代码会改变如下:
var map = roots.SelectMany(root => flatten(root)).ToDictionary(x => x.ID);
答案 1 :(得分:0)
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));
答案 2 :(得分:0)
如果您的树很少变异,出于性能目的,我会“保存”另外两个字段:“左”和“右”字段。使用此处描述的算法:
http://www.sitepoint.com/hierarchical-data-database-2/
获取灵感。 ideea很简单:您只想从一个数据库中调用数据库中的所有树,并在内存中以O(n)时间构建它。如果您有这种感觉,可以保留“ParentID”字段并将其用于构建内存中的图形。缺点是重建树需要花费O(N)时间(当插入/移除节点或在树中的另一个位置移动时,必须重新计算Left和Right值)。否则我会使用数据库特定技术(CTE)
示例:CTE Recursion to get tree hierarchy
我们也要关注性能!这非常重要。