1
votes

I have an IList<Category>

The Category type is coming from my Category Table in SQL Server:

Table: Category

CategoryID, ParentCategoryID

so typical heirarchy in one table.

So if I have this IList of Categories that contain the node relationships, then I am trying to figure out how this fits into making a Node and Tree like a lot of people are doing. And how would the constructor look and this class be created in terms of usage?

I think it makes sense here to create a generic Node class and Tree class so I can reuse in the future I think for other tree types.

So if I have something like this, how is T being used and what am I benefiting from?

(pseudo code here)

public class Node<T>
...
    Node<T> _parentNode;
    List<Node<T>> _children;

    private void SetParentNode(T)
    private void AddChild(T)

... etc.

trying to understand the concept here on why a Generic Node class would be used for any type coming in like Childeren, etc. that contains a child/parent relationship (int Ids)

UPDATE

So having an issue here with the GroupBy recommendation. Check out what I tried to do with your example:

First I have this property in my Tree class:

public Dictionary, IList>> ParentNodeAndRelatedChildrenFlattenedMap { get; private set; }

and incoming to my class's constructor is a IList dependencies that I converted (looped and created a new Node for every one of them) to a IList>

Now I'm trying to group that list by Node.ParentId as you were talking so that I get a grouping on Parent Nodes and since each node has its children property it's easy to find out what the related children are to those parent nodes.

But here is the problem now later down in my code:

public void CreateFlattenedMap()
{
    var parentGroups = _nodeDependencies.GroupBy(d => d.ParentNodeId);

    var dictionary = parentGroups.ToDictionary(d => d, d => d.ToList());

    ParentNodeAndRelatedChildrenFlattenedMap = dictionary;
}

well it's not liking my assignment of dictionary because it's an > dictionary that's created by the ToDictionary(). So not sure how to get this grouping grouped and to a dictionary that is a , List> where Node in the dictionary is the Node instance for that parent I'm grouping on (yea I'm grouping on its Node.ParentId but I want the Node though in the ToDictionary in the end) and the List> is the list of Children Nodes from the Parent Node.Children property.

3

3 Answers

3
votes

This is what a more complete implementation would look like:

public class TreeNode<T>
{
    private T _item;
    private TreeNode<T> _parentNode;
    private List<TreeNode<T>> _children;

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

    public void SetParentNode(T parent)
    {
        _parentNode.Item = parent;
    }

    public T Item
    {
        get { return _item; }
        set { _item = value; }
    }

    public void AddChild(T child)
    {
        _children.Add(new TreeNode<T>(child));
    }

    public void RemoveChild(T child)
    {
        var node = _children.FirstOrDefault(e => e.Item.Equals(child));
        if (node != null)
            _children.Remove(node);
    }
}

And your questions:

How would the constructor look and this class be created in terms of usage?

As you can see from the above in AddChild(), simply specify the type of the child for the constructor.

var node = new TreeNode<T>(item);

So if I have something like this, how is T being used and what am I benefiting from?

The use of generics can same a lot of time rewriting code when done right. In the implementation above, we can make a tree structure out of basically any type we want by simply changing T. So this saves us a lot of time if we need a tree structure for more than one type.

var intTreeNode = new TreeNode<int>(10);
var stringTreeNode = new TreeNode<string>("hello world");

Generically really take some exposure time before it clicks and you "just get it", keep at it.

Constructing the tree

To construct the tree from a list of categories that may or may not have parent categories you will need to iterate through the list in some way. A decent way to go about this would be to first organise them into groupings based on the ParentCategoryID and constructing the tree. Something like this (untested):

public List<TreeNode<Category>> ConstructCategories(List<Category> categories)
{
    var groups = categories.GroupBy(e => e.ParentCategoryID);
    var rootGroup = groups.Single(e => e.Key == null);
    var categories = List<TreeNode<Category>>();

    foreach (var category in rootGroup)
    {
        // Create and fill category
        var node = new TreeNode<Category>(category);
        ConstructChildrenCategories(node, groups);
        categories.Add(node);
    }
}

public void ConstructChildrenCategories(TreeNode<Category> node, IEnumerable<IGrouping<Category>> groups)
{
    var group = groups.Single(e => e.Key == node.Item.CategoryID);

    foreach (var category in group)
    {
        // Create and fill category
        var childNode = new TreeNode<Category>(category);
        ConstructChildrenCategories(childNode, groups);

        // We could do this automatically in both methods.
        childNode.SetParent(node.Item);
        node.AddChild(childNode);
    }
}
2
votes

This may be helpful:

public interface ICategory
{
    int Id { get; }
    int ParentId { get; }
}

public class Category : ICategory
{
    int id;
    int parentId;

    public int Id { get { return id; }}
    public int ParentId { get { return parentId; }}
}

And this is the Node class:

public class Node<T> where T : ICategory
{
    Node<T> _parentNode;
    List<Node<T>> _children;

    public Node<T> Parent { get { return _parentNode; }}

    public Node<T> Child(int index) { return _children[index]; }

    public T Value;

    public Node(T value)
    {
        this.Value = value;
    }

    public void AddChild(T item)
    {
        Node<T> child = new Node<T>(item);
        this._children.Add(child);
        child._parentNode = this;
    }
}

And this is the Tree class:

public class Tree<T> where T : ICategory
{
    List<Node<T>> values;

    public Tree()
    {
        this.values = new List<Node<T>>();
    }

    public Node<T> FindNode(int id)
    {
        if (values.Exists(input => input.Value.Id == id))
        {
            return values.Find(input => input.Value.Id == id);
        }
        else { return null; }

    }

    public void AddNode(T value)
    {
        Node<T> parent = FindNode(value.ParentId);

        if (parent != null)
        {
            parent.AddChild(value);
        }
    }
}
-1
votes

You can't make all the function of tree and treenode.

rather add the class data to Treenode.

for example

class MyClass
{
    public int A;
    public string B;
}

...

TreeNode Node = TreeView.Nodes.Add("AAA");
MyClass Data = new MyClass();
Node.Tag = Data;

The Data assigned to TreeNode will not be deleted. and you can use whole tree property and Nodes member, too.

the only thing that you should do is typecast when use data of the node.

TreeNode Node = TreeView.Nodes[0];
MyClass Temp = Node.Tag as MyClass;