2

In my ASP.NET application, I want to convert List of objects in to a Tree like structure (i.e. to connect every leaf node under it's corresponding parent). I have tried this but not getting the desired structure:

public class CVBoxController : ApiController
{
    public async Task<HttpResponseMessage> GetCategories()
    {
        var categories = new List<CategoriesList>
            {
                new CategoriesList { Id=1, Name="Cat 01", ParentId=0 },
                new CategoriesList { Id=2, Name="Cat 02", ParentId=0 },
                new CategoriesList { Id=3, Name="Cat 03", ParentId=0 },
                new CategoriesList { Id=4, Name="Cat 04", ParentId=0 },
                new CategoriesList { Id=5, Name="Cat 05", ParentId=0 },
                new CategoriesList { Id=6, Name="Cat 06", ParentId=1 },
                new CategoriesList { Id=7, Name="Cat 07", ParentId=6 },
                new CategoriesList { Id=8, Name="Cat 08", ParentId=3 },
                new CategoriesList { Id=9, Name="Cat 09", ParentId=4 },
                new CategoriesList { Id=10, Name="Cat 10", ParentId=9 },
                new CategoriesList { Id=11, Name="Cat 11", ParentId=10 }
            };

        var response = CreateCategoryTree(categories);
        return Request.CreateResponse(HttpStatusCode.OK, response);
    }

    private List<CategoryNode> CreateCategoryTree(List<CategoriesList> categories)
    {
        List<CategoryNode> nodes = new List<CategoryNode>();

        foreach (var item in categories)
        {
            if (item.ParentId == 0)
                nodes.Add(new CategoryNode { Id = item.Id, Name = item.Name });
            else
            {
                CreateNode(nodes, item);
            }
        }
        return nodes;
    }

    private void CreateNode(List<CategoryNode> nodes, CategoriesList parent)
    {
        foreach (var node in nodes)
        {
            if (node.Id == parent.Id)
            {
                node.Children.Add(new CategoryNode { Id = parent.Id, Name = parent.Name });
            }
            else
            {
                CreateNode(node.Children, parent);
            }
        }
    }
}

public class CategoriesList
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int ParentId { get; set; }
}

public class CategoryNode
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<CategoryNode> Children { get; set; }

    public CategoryNode()
    {
        Children = new List<CategoryNode>();
    }
}

Current response (as JSON):

[{
    "id": 1,
    "name": "Cat 01",
    "children": []
}, {
    "id": 2,
    "name": "Cat 02",
    "children": []
}, {
    "id": 3,
    "name": "Cat 03",
    "children": []
}, {
    "id": 4,
    "name": "Cat 04",
    "children": []
}, {
    "id": 5,
    "name": "Cat 05",
    "children": []
}]

As you see the JSON is not nested (children not populated). It should be according to what populated in List<CategoriesList> above. ParentId=0 means these are the root nodes with no parents above.

Ali Shahzad
  • 5,163
  • 7
  • 36
  • 64

1 Answers1

1

Ok, your problem is in here and is a symptom of your confusing parameter naming:

private void CreateNode(List<CategoryNode> nodes, CategoriesList parent)
{
    foreach (var node in nodes)
    {
        if (node.Id == parent.Id)
        {
            node.Children.Add(new CategoryNode { Id = parent.Id, Name = parent.Name });
        }
        else
        {
            CreateNode(node.Children, parent);
        }
    }
}

This is called when a node isn't a top level node. It's supposed to find the parent and add the new node as a child. But the problem is that the parameter parent is actually the item you are adding. So this:

if (node.Id == parent.Id)

Can never be true. Because you node the node with the Id matching the item you are adding hasn't been added yet! There is no node in nodes that matches the Id of the item you are adding (unless your list can contain duplicate id's, which I'm assuming isn't the case)

It should be something like:

if (node.Id == parent.ParentId)

Which wouldn't have tripped you up if you'd name the second parameter to CreateNode something like item or newNode, or anything other than parent.

Here's a fiddle that seems to work correctly.

Matt Burland
  • 44,552
  • 18
  • 99
  • 171