Question

I have this object:

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

I want to calculate using a lambda expression, the depth of the object, how many layers of the object in itself exists?

I saw this JavaScript post, but I am struggling to translate it to a one line lambda statement.

Lets say the object is as this new dtHeader(){ ParentHeader = null, HeaderText = "col1" }; the result would be 1

and for new dtHeader(){ ParentHeader = new dtHeader(){ ParentHeader = null, HeaderText = "col1" }, HeaderText = "col1" }; the result would be 2

I want to achieve this with a list<dtHeader>, so some of them would have a depth of 1 and others with deeper depths, and want the deepest depth.

    _______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_|___|

It must go infinitly(Until where it allows for objects to heap up inside eachother) deep.

var HeaderLayerCount = lDtCol.Where(n => n.ParentHeader != null)
                             .Where(n => n.ParentHeader.ParentHeader != null)
                             .Where(n => n.ParentHeader.ParentHeader.ParentHeader != null);

EDIT: I just want to add that if you want to work on a specific depth level, for instance, all objects on a depth of 3, you can use this extra recursion function in the class

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

You can use it like so:

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
}
Was it helpful?

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

OTHER TIPS

Why not implementing a int GetDepth() method on your class, that will reach the top most ancestor, counting each level?

Your query would then be much simpler.

I was outrunned by Frode, kudos to him

I had the same implementation:

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

Here's a lambda expression to get what you want:

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

You have to define it in two parts (assigning null & assigning the body) to let recursion work.

I modified Enigmativity's answer to make it work correctly:

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

    return depth;
};

Call it like this:

int depth = getDepth(header, 0)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top