Click here to Skip to main content
15,886,788 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I have a data table as following

ID    Name   ParentID
1      A       0
2      B       1
3      C       1
4      D       2
5      E       3
6      F       4


I want this as a List<chart>

C#
//Chart.cs
 public int ID { get; set; }
 public string Name { get; set; }
 public string ParentID{ get; set; }
 public List<chart> lstChild{ get; set; }


What I have tried:

I have tried this function:

C#
public static void MakeTree(DataTable dt, clsChart parent)
        {
            foreach (DataRow row in dt.AsEnumerable().Where(x => x.Field<int>("ParentID") == parent.ID))
            {
                if (parent.lstChildren == null)
                {
                    parent.lstChildren = new List<clschart>();
                }
                clsChart newNode = new clsChart();
                newNode.Name = row.Field<string>("Name");
                newNode.ID = row.Field<int>("EntryID");
                parent.lstChildren.Add(newNode);
                MakeTree(dt, newNode);
            }
        }


But it doesn't return List<>, it returns the class
Posted
Updated 28-Mar-19 19:36pm
v2

As to me, your datatable is designed properly for hierarchical data model[^]. That's why a chart model should reflect to a datatable model.
C#
public class chart
{
	public int ID { get; set; }
 	public string Name { get; set; }
	public int ParentID{ get; set; }
}


This is very easy to create a List<chart> from your datatable:
List<chart> charts = dt.AsEnumerable()
    .Select(dr=> new chart()
        {
            ID=dr.Field<int>("ID"),
            Name=dr.Field<string>("Name"),
            ParentID=dr.Field<int>("ParentID")
        })
    .ToList();


Above model is a "flat" version of hierarchical data. In case you want to create more advanced hierarchical structure, please, read this: Recursive Hierarchical Joins in C# and LINQ - Bitlush[^]
 
Share this answer
 
You can recursively create (and link) your nodes, but you also need a "master list" that you add to when you create each node.

The "master list" represents the fully expanded tree; a node is expandable / collapsible if it has children.

"Canned" tree views maintain an internal "tree view list" (same thing).

The "level" of each item determines the indenting.

Adding a "level number" is useful when creating tree. With a level number, you can also infer the parent, if any, when using compound keys.
 
Share this answer
 
Firstly, ParentID should be an int, not a string

Secondly, you don't need recursion at all, and provided your parent nodes are always defined before their children, you can use a single loop. (If it's a random order, then create a node collection first, and then loop through that afterwards:
class Chart
    {
    private static Dictionary<int, Chart> all = new Dictionary<int, Chart>();
    public int ID { get; set; }
    public string Name { get; set; }
    public int ParentID { get; set; }
    public List<Chart> Children { get; set; } = new List<Chart>();
    private Chart(DataRow row)
        {
        ID = (int)row["ID"];
        Name = (string)row["Name"];
        ParentID = (int)row["ParentID"];
        all[ID] = this;
        }
    public static Chart MakeTree(DataTable dt)
        {
        List<Chart> nodes = new List<Chart>();
        Chart root = null;
        foreach (DataRow row in dt.AsEnumerable())
            {
            Chart node = new Chart(row);
            if (node.ParentID == 0)
                {
                if (root != null) throw new ArgumentException("Too many ROOT nodes: only one was expected.");
                root = node;
                }
            else
                {
                if (root == null) throw new ArgumentException("ROOT node has not been defined: one is required.");
                Chart parent = all[node.ParentID];
                parent.Children.Add(node);
                }
            }
        return root;
        }
    }

DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(int));
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("ParentID", typeof(int));
dt.Rows.Add(1, "A", 0);
dt.Rows.Add(2, "B", 1);
dt.Rows.Add(3, "C", 1);
dt.Rows.Add(4, "D", 2);
dt.Rows.Add(5, "E", 3);
dt.Rows.Add(6, "F", 4);
Chart root = Chart.MakeTree(dt);
 
Share this answer
 
Comments
prapti.n3 27-Mar-19 4:23am    
But I want it in List<chart> type not as Chart object
OriginalGriff 27-Mar-19 4:36am    
You have one root node: this contains a List of all the child Chart items. The only List of all items you could create would contain one item: the root node. All the others are child nodes of that root.

So why would you want that at all?
prapti.n3 27-Mar-19 4:40am    
Because this is only an example. The data on which i am working has 3-4 root with parent ID 0.
OriginalGriff 27-Mar-19 4:56am    
So create a "dummy node" with ID 0, and then return the child list from that!

Or, add a collection of nodes to return, and add all nodes with a parent of 0 to that instead of limiting it to one root.
List<clschart> lstChart = new List<clschart>();
string JSONresult;

//var roots = dt.AsEnumerable().ToList();
lstChart = clsDataTables.ConvertDataTable<clschart>(dt);
var roots = lstChart.Where(f => f.ParentID == 0).ToList();
//(from n in dt.AsEnumerable() where n.Field<int>("ParentID") == 0 group n by n.Field<int>("ParentID")).ToList();
foreach (var root in roots)
clsChart.BuildTree(root, lstChart);

JSONresult = JsonConvert.SerializeObject(roots, Formatting.Indented);
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900