LINQ向上遍历并检索父子关系

时间:2016-01-12 10:41:48

标签: linq c#-4.0

我有一个树形结构,格式如下:

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的树结构中的子关系。

任何想法?

3 个答案:

答案 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)然后我得到这个结果:

result

或者,如果我使用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

我们也要关注性能!这非常重要。