使用Lambda表达式获取具有相同类型的对象树的深度

3

我有这个对象:

public class dtHeader
{
    public dtHeader ParentHeader { get; set; }
    public string HeaderText { get; set; }
    public string DataField { get; set; }
    public bool Visible { get; set; }
    public int DisplayOrder { get; set; }
}

我想使用Lambda表达式来计算对象的深度,即这个对象内部有多少层存在?

我看到了这篇JavaScript帖子, 但我无法将其翻译为一行Lambda语句。

比如说,对于这个对象new dtHeader(){ ParentHeader = null, HeaderText = "col1" },结果将会是1。

而对于new dtHeader(){ ParentHeader = new dtHeader(){ ParentHeader = null, HeaderText = "col1" }, HeaderText = "col1" },结果将会是2。

我希望通过一个list<dtHeader>来实现这个目标,因此其中一些对象的深度为1,而其他对象具有更深的深度,并且想要找到最深的深度。

    _______ITEM_IN_LIST_OBJECT__
    ______1___2___3___4___5___6_
 D  1.  |_o_|_o_|_o_|_o_|_o_|_o_|
 E  2.  |_o_|___|_o_|___|_o_|_o_|
 P  3.  |___|___|_o_|___|_o_|___|
 T  4.  |___|___|___|___|_o_|___|
 H  5.  |___|___|___|___|_o_|___|

它必须无限地向下延伸(直到允许物体相互堆叠的位置)。

var HeaderLayerCount = lDtCol.Where(n => n.ParentHeader != null)
                             .Where(n => n.ParentHeader.ParentHeader != null)
                             .Where(n => n.ParentHeader.ParentHeader.ParentHeader != null);
编辑: 我只想补充一点,如果你想在特定深度级别上工作,例如所有深度为3的对象,你可以在类中使用这个额外的递归函数。
public class dtCol
{
    public dtCol ParentHeader { get; set; }
    public string HeaderText { get; set; }
    public string DataField { get; set; }
    public bool Visible { get; set; }
    public int DisplayOrder { get; set; }
    public int Depth { get { return ParentHeader != null ? ParentHeader.Depth + 1 : 1; } }
    public int CurrentDepth { get; set; } //Set on initialisation
    public dtCol getParent(dtCol col, int getDepth) //Gets the parent on a specific level after the first base level (1) else returns the previous not null child
    {
        return (col.ParentHeader != null && col.ParentHeader.CurrentDepth == getDepth) ? col.ParentHeader : this.getParent(col.ParentHeader, getDepth);
    }
}

你可以这样使用它:

var HeaderLayerCount = lDtCol.OrderByDescending(n => n.Depth).First().Depth;
for (int hlc = 1; hlc <= HeaderLayerCount; hlc++)
{
    var headerrow = new List<dtCol>();
    //This foreach adds the parent header if not null else adds the not null child
    lDtCol.ForEach(n =>
    {
        var h = n.getParent(n, hlc); //Get Parent, null is returned if parent does not exists
        headerrow.Add((h != null) ? h : n); //If parent is null, add base dtCol so that the headers can be merged upwards.
    });

    //Do what you need with your new single dimensional list of objects
}
4个回答

4

为什么不在您的类上实现一个int GetDepth()方法,该方法将到达最顶层的祖先并计算每个级别?

这样,您的查询将会更简单明了。

我被Frode超过了,向他致敬。

我有同样的实现:

 public int GetDepth()
        {
            if (ParentHeader == null)
            {
                return 1;
            }
            else return 1 + ParentHeader.GetDepth();
        }

听起来是一个非常好的想法,你可以发一个例子来精确地说明如何去做吗? - Pierre

2
using System;
using System.Linq;

namespace ConsoleApplication3
{
    public class dtHeader
    {
        public dtHeader ParentHeader { get; set; }
        public string HeaderText { get; set; }
        public string DataField { get; set; }
        public bool Visible { get; set; }
        public int DisplayOrder { get; set; }
        public int Depth
        {
            get
            {
                // If header has parent, then this depth is parent.depth + 1
                if (ParentHeader != null)
                    return ParentHeader.Depth+1;
                else
                    return 1; // No parent, root is depth 1
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            dtHeader[] headers = { 
                                     new dtHeader { HeaderText = "dt1" },
                                     new dtHeader { HeaderText = "dt2" },
                                     new dtHeader { HeaderText = "dt3" },
                                     new dtHeader { HeaderText = "dt4" },
                                     new dtHeader { HeaderText = "dt5" }
                                 };

            headers[1].ParentHeader = headers[0];
            headers[2].ParentHeader = headers[1];
            headers[3].ParentHeader = headers[2];
            headers[4].ParentHeader = headers[3];

            var deepest = headers.OrderByDescending(item=>item.Depth).First();
            Console.WriteLine(deepest.Depth+ ", " + deepest.HeaderText);

            var runner = deepest;
            while (runner.ParentHeader != null)
                runner = runner.ParentHeader;

            Console.WriteLine("The deepest root header is:" + runner.HeaderText);
        }
    }
}

2
这里有一个lambda表达式可以帮你得到想要的结果:
Func<dtHeader, int> getDepth = null;
getDepth = dth =>
{
    var depth = 1;
    if (dth.ParentHeader != null)
    {
        depth += getDepth(dth.ParentHeader);
    }
    return depth;
};

为了让递归起作用,您需要将其定义为两部分(分配 null 和分配主体)。


这将始终返回1。您需要通过参数将深度传递给getDepth函数。 - michal.jakubeczy
@jabko87 - 不,这个函数完全正常。它的想法是返回深度 - 它不需要传递深度来完成这个任务。 - Enigmativity

0

我修改了Enigmativity的答案,使其正确运行:

Func<dtHeader, int, int> getDepth = null;
getDepth = (dth, depth) =>
{
    if (dth.ParentHeader != null)
    {
        depth = getDepth(dth.ParentHeader, ++depth);
    }

    return depth;
};

这样调用:

int depth = getDepth(header, 0)

这完全是不必要的。我的函数运行得非常好。 - Enigmativity
你的代码实际上是错误的。你正在调用 getDepth(header, 0),它将返回比所需值少1。你的代码要求调用者知道初始深度。 - Enigmativity

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接