简体   繁体   中英

Generic Tree Structure - How to populate an org chart

I have what should be a fairly trivial problem but I want to make sure I'm doing this in the most "elegant" way possible in .Net 4.5 and I'd like some opinions from people smarter than me.

I have a class that represents a generic tree structure as such:

   public class TreeNode<T>
    {
        List<TreeNode<T>> Children;

        T Item {get;set;}

        public TreeNode (T item)
        {
            Item = item;
        }

        public TreeNode<T> AddChild(T item)
        {
            TreeNode<T> nodeItem = new TreeNode<T>(item);
            Children.Add(nodeItem);
            return nodeItem;
        }
    }

Now, I have a Person class that represents an employee of the organization. Each Person object has an ID and a BossID that points to their superior.

Multiple employees can have the same boss, hence why I am trying to create an organization chart with this tree structure.

The top node is going to be the Person object where BossID is null (it's an int? ). That I can quickly get with LINQ.

It's the next step that is puzzling me a bit. There are multiple approaches but they seem somewhat sloppy to me and I know there must be a much easier/elegant way to fill in the rest of the org chart.

So right now I have a generic object of List<Person> holding all the employees, with their various BossID s and this generic tree structure that I can add child nodes to.

It's all very basic, but what is the proper sequence to fill the tree? Am I recursively supposed to iterate down the line? I know there is backtracking involved here, which is where I'm getting stumped.

I apologize, my background is not in Computer Science, and if it was I realize that tree structures, linked lists, and everything else is trivial stuff. But this is my first attempt and I want to see how it's done properly.

I appreciate any guidance.

So, given you have a Person class defined as this:

public class Person
{
    public int ID;
    public int? BossID;
}

...and you have a List<Person> defined as people then this works:

var lookup = people.ToLookup(p => p.BossID);

Action<TreeNode<Person>> addChildren = null;
addChildren = p =>
{
    foreach (var child in lookup[p.Item.ID])
    {
        var childNode = p.AddChild(child);
        addChildren(childNode);
    }
};

var trees =
    from boss in lookup[null]
    select new TreeNode<Person>(boss);

foreach (var tree in trees)
{
    addChildren(tree);
}

This assumes that you may have more than one person with a null boss. If you don't that's fine, just run this code and do trees.First() .

The definition of TreeNode<T> I used was this:

public class TreeNode<T>
{
    private List<TreeNode<T>> Children;

    public T Item { get; set; }

    public TreeNode(T item)
    {
        this.Item = item;
        this.Children = new List<TreeNode<T>>();
    }

    public TreeNode<T> AddChild(T item)
    {
        var nodeItem = new TreeNode<T>(item);
        this.Children.Add(nodeItem);
        return nodeItem;
    }
}

You could shorten TreeNode<T> to this though:

public class TreeNode<T> : List<TreeNode<T>>
{
    public T Item { get; set; }

    public TreeNode(T item)
    {
        this.Item = item;
    }
}

...then your need to modify addChildren to this:

Action<TreeNode<Person>> addChildren = null;
addChildren = p =>
{
    foreach (var child in lookup[p.Item.ID])
    {
        var childNode = new TreeNode<Person>(child);
        p.Add(childNode);
        addChildren(childNode);
    }
};

...but then you'd have all of the standard List<> operators available for TreeNode<T> .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM