Получить глубину дерева объектов одного типа с помощью лямбда-выражения


У меня есть этот объект:

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; }
}

Я хочу вычислить, используя лямбда-выражение, глубину объекта, сколько слоев объекта в себе существует?

Я видел Этот пост JavaScript, но я изо всех сил пытаюсь перевести его в однострочный лямбда-оператор.

Предположим, что объект таков 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 3

4 ответа:

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);
        }
    }
}

Почему бы не реализовать метод int GetDepth () в вашем классе, который достигнет самого верхнего предка, считая каждый уровень?

Тогда ваш запрос будет намного проще.

Меня обогнал Фроде, слава ему

У меня была такая же реализация:

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

Вот лямбда-выражение, чтобы получить то, что вы хотите:

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

Вы должны определить его в двух частях (назначение null и назначение тела), чтобы позволить рекурсии работать.

Я изменил ответ 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)