5

I have been stuck on this problem for a few days and would appreciate some ideas or help in resolving it. I have a collection of objects

 public class Hierarchy
{
    public Hierarchy(string iD, string name, int level, string parentID, string topParent)
    {
        ID = iD;
        Name = name;
        Level = level;
        ParentID = parentID;
        Children = new HashSet<Hierarchy>();
    }
    public string ID { get; set; }
    public string Name{ get; set; }
    public int Level { get; set; }
    public string ParentID { get; set; }
    public ICollection<Hierarchy> Children { get; set; }
}

The data from the Linq Query to my Entity is:

ID      Name     Level ParentID
295152  name1    1     null
12345   child1   2     295152
54321   child2   2     295152
44444   child1a  3     12345
33333   child1b  3     12345
22222   child2a  3     54321
22221   child2b  3     54321
22002   child2c  3     54321
20001   child2a2 4     22222
20101   child2b2 4     22222

This data could extend to an unknown depth of levels (I'm only showing 4). Ultimately I would have one Hierarchy object with a collection of multiple child objects which in turn may have a collection of multiple child objects...etc... There will always only be one top level object.

I am trying to use Linq as much as possible in this project.

This obviously needs some sort of recursive method but I'm stuck. Any ideas or help would be appreciated.

TIA

John S
  • 7,909
  • 21
  • 77
  • 145

2 Answers2

4

Actually, an iterative solution is probably far easier. Here are the steps:

  1. Hash all the nodes into a dictionary based on their id
  2. Loop through a second time, and add each node to its parent's child list

Which looks like so:

Hierarchy CreateTree(IEnumerable<Hierarchy> Nodes)
{
    var idToNode = Nodes.ToDictionary(n => n.ID, n => n);

    Hierarchy root;
    foreach (var n in Nodes)
    {
        if (n.ID == null)
        {
            if (root != null)
            {
                //there are multiple roots in the data
            }
            root = n;
            continue;
        }

        Hierarchy parent;
        if (!idToNode.TryGetValue(n.ID, parent))
        {
            //Parent doesn't exist, orphaned entry
        }

        parent.Children.Add(n);
    }

    if (root == null)
    {
        //There was no root element
    }
    return root;
}

There's a couple obvious possible error conditions with your data format. Its up to you what do with them.

In general, there is always an iterative solution and a recursive solution. The particular problem changes which one is easier.

Chris Pitman
  • 12,990
  • 3
  • 41
  • 56
  • I really like your response and description. I wish I could make two answers. – John S Jan 11 '13 at 16:53
  • Hi, thank you for you solution. I tried to use your CreateTree method but I have a problem of reference. I exposed it here https://stackoverflow.com/questions/54747804/why-sometimes-2-objects-reference-the-same-but-not-always – Florian Feb 18 '19 at 12:55
4

You can try this recursive function:

void PopulateChildren(Hierarchy root, ICollection<Hierarchy> source)
{
    foreach (var hierarchy in source.Where(h => h.ParentID == root.ParentID))
    {
        root.Children.Add(hierarchy);
        PopulateChildren(root, source);
    }
}

Which you can use like this:

ICollection<Hierarchy> hierarchies = new List<Hierarchy>(); // source

// Get root
var root = hierarchies.Single(h => h.Level == 1);

// Populate children recursively
PopulateChildren(root, hierarchies);
OJ Raqueño
  • 4,471
  • 2
  • 17
  • 30
  • 1
    There was a slight mod needed to make this work. in the internal call to PopulateChildren hierarchy needs to be passed as it is the "root" at this level. Then it all works just great. – John S Jan 11 '13 at 16:52
  • This illustrates the tradeoff between recursion and iteration for some problems: This recursive solution is O(n^2), while the iterative solution is O(n). – Chris Pitman Jan 11 '13 at 17:19