Search code examples
c#recursiontreehierarchical

Numbering Item on Hierarchical List C#


I have a hierarchical list of type Element. Each Element has a ParentElement and a Collection of ChildElements.

I have written a recursive HTML helper to loop through the hierarchical list and print out each Element into a table row. I want to be able to label each of the Elements in the table based on their position in the hierarchical list. For example

Example List

Substructure has no child elements

Superstructure has 3 ChildElements - Upper Floors, Frame and Roof

Upper Floors and Frame have no child elements

Roof has 2 ChildElements - Roof Coverings and Roof Structure

I have the following HTML on a view that calls the recursive helper.

<table class="table table-bordered">
     <thead>
         <tr>
             <td>Reference</td>
             <td>Name</td>
         </tr>
     </thead>
     <tbody>
         @ShowTreeTable(Model.Elements, 1)
     </tbody>
</table>

The recursive helper is as follows.

@helper ShowTreeTable(List<Entities.Entities.Element> elements, int level)
{
    for (int i = 0; i < elements.Count(); i++)
    {
        <tr>
            <td>@level</td>
            <td>@elements[i].Name</td>
        </tr>

        if (elements[i].ChildElements.Any())
        {
            @ShowTreeTable(elements[i].ChildElements.ToList(), level + 1)
        }
    }
}

I had a more elaborate helper while trying to do this but it wasn't close so I stripped it back to this.

With the current example the results are coming out like so.

Current Example Results


Solution

  • I got this working by manipulating the the tree in C# before it's put in the view.

    First I got the root elements and set their reference property to their position in the list (+1).

    var roots = this.Elements.Where(x => !x.ParentElementId.HasValue).ToList();
    
    this.Elements.Where(x => !x.ParentElementId.HasValue).ToList()
        .ForEach(x => x.Reference = (roots.IndexOf(x) + 1).ToString());
    

    I then wrote another recursive method to update all the child element references. The tricky part on this was to update the all the children parent element before they where given their own reference. If this wasn't done all the child elements contained a reference to a parent element that didn't have its updated reference.

    private void ShowTree(List<Entities.Entities.Element> elements)
    {
        for (int i = 0; i < elements.Count(); i++)
        {
            if (elements[i].ParentElementId.HasValue)
            {
                elements[i].Reference = $"{elements[i].ParentElement.Reference}.{i+1}";
            }
    
            if (elements[i].ChildElements.Any())
            {
                foreach (var child in elements[i].ChildElements)
                {
                     child.ParentElement = elements[i];
                }
    
                this.ShowTree(elements[i].ChildElements.ToList());
            }
        }
    }
    

    This combined with the HTML code in the OP resulted in the correct hierarchical references.

    enter image description here