7

I know this must be real easy but I just can't get it to work... I am trying to make a List for extjs Tree by comparing two columns from differnt rows and putting them as a Node or a leaf accordingly. this is my sample data

ListA  ListB  labelName
 NY           Parent1
        NY    Leaf1
 HI           Parent2
 AK           Parent3

and this is my c# end... so when I match NY, I am supposed to have Parent1 as node and Leaf1 as its leaf and not for HI or AK... but doing this throws me all data as Parent.. even the leaf.

            SqlCommand cmd = con.CreateCommand();

            comd.CommandText = "SELECT * FROM myTable";
            con.Open();
            SqlDataReader reader = comd.ExecuteReader();
            while (reader.Read())
            {
                City MyData = new City();

                MyData.ListA = reader["ListA"].ToString().Trim();
                MyData.ListB = reader["ListB"].ToString().Trim();
                MyData.labelName = reader["labelName"].ToString().Trim();
                giveData.Add(MyData);
            }

            int count = 1;

            List<TreeNode> myNode = new List<TreeNode>();
            foreach (City MyData in giveData)
            {
                // 1st foreach
                    if (MyData.ListA != "")
                    {

                        TreeNode treeNode = new TreeNode();
                        treeNode.id = count++;
                        treeNode.name = MyData.labelName;
                        treeNode.leaf = false;

                        List<TreeNode> Level1 = new List<TreeNode>();
                        foreach (City labelName  in giveData)
                        {
                            if (labelName.ListA == labelName.ListB)
                            {// 2nd foreach
                                TreeNode node1 = new TreeNode();
                                node1.id = count++;
                                node1.name = labelName.labelName;
                                node1.leaf = true;

                                Level1.Add(node1);
                            }
                        }

                        treeNode.children = Level1;
                        myNode.Add(treeNode);
                }
            }
            return JsonConvert.SerializeObject(myNode);

Should I use array to store each record and compare them instead... I am out of ideas... I am sure there is a better way to accomplish this... Please help

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
EagleFox
  • 1,367
  • 10
  • 34
  • 58
  • ... I'm a bit confused - How would you know which node the leaf "NY" (Leaf1) goes with? Based upon the number (Leaf1 goes with Parent1) or by the location in the list? ... will a leaf node always be a blank in List A? – John Bustos Dec 26 '12 at 21:49
  • Hi John... if the value matches, ListA goes as Parent and ListB goes as leaf – EagleFox Dec 26 '12 at 21:50
  • And so you want a tree that looks as follows Node1:Parent1 - Leaf1; Node2: Parent2; Node3: Parent2? I'm just a bit confused on the final output?? – John Bustos Dec 26 '12 at 21:54
  • Yes, anything that is on list B goes as a Leaf..so in the above data... Node1:Parent1 - Leaf1; Node2: Parent2; Node3: Parent3... I just realized a typo in my data.. should be Parent3 – EagleFox Dec 26 '12 at 21:57
  • 1
    ... I posted a solution in VB, but looking at your code (just by guessing), if you replaced `if (labelName.ListA == labelName.ListB)` to `if (MyData.ListA == labelName.ListB)`, I think it would also work... – John Bustos Dec 26 '12 at 22:13
  • Thanks John. I followed what you said here. It works but takes a lot of time to load the tree and crashes. But I have to keep running the script to make it work. Is there a solution to this performance issue. – EagleFox Dec 27 '12 at 01:15
  • I'm a VB guy, but I'll try and update your code and post a new solution in C#... HOPEFULLY I'll get it working - All I can promise, though, is that the logic will be sound :) – John Bustos Dec 27 '12 at 13:44
  • :) Thanks John... I mean i get the results but keeps crashing... I can't think of another way of doing it. I am trying to see if getting the data from the db in a different way may be faster... – EagleFox Dec 27 '12 at 13:58
  • I've updated my answer with what I think will be a more efficient way of combining the leaves with parents. – CAbbott Dec 27 '12 at 14:56

3 Answers3

3

Assuming the data is the way you state and the "parents" will come before any leaves, here's a single pass way I came up with to create the tree:

[Irrelevant Code Snipped]

Update: LINQ using Dictionary<string, List<TreeNode>>

I created a new class TreeNode and some sample data for testing:

var MyData = new List<City>
                  {
                     new City {ListA = "AK", ListB = "", labelName = "Alaska"},
                     new City {ListA = "HI", ListB = "", labelName = "Hawaii"},
                     new City {ListA = "", ListB = "HI", labelName = "Hawaii Leaf 1"},
                     new City {ListA = "", ListB = "HI", labelName = "Hawaii Leaf 2"},
                     new City {ListA = "NY", ListB = "", labelName = "New York"},
                     new City {ListA = "", ListB = "NY", labelName = "New York Leaf 1"},
                     new City {ListA = "", ListB = "NY", labelName = "New York Leaf 2"}
                  };

Here's the new method that basically creates 2 lists, 1 for the parents and 1 for the leaves. I then loop through the leaves to find any matching parents and add the leaves to it:

var index = 0;
var parents = (from p in MyData
               where p.ListB == ""
               select p).ToDictionary(p => p.ListA, p => new TreeNode { id = index++, name = p.labelName, leaf = false });

var leaves = (from l in MyData
              where l.ListA == ""
              group l by l.ListB into stateGroup
              select stateGroup).ToDictionary(g => g.Key, g => g.ToList());

foreach (var leaf in leaves.Where(leaf => parents.ContainsKey(leaf.Key)))
{
    parents[leaf.Key].children =
        leaf.Value.Select(l => new TreeNode {id = index++, name = l.labelName, leaf = true}).ToList();
}

var myNode = parents.Select(p => p.Value).ToList();

return JsonConvert.SerializeObject(myNode);

I think this should be more efficient than using lists and the List.Find()

CAbbott
  • 8,078
  • 4
  • 31
  • 38
  • Thank you CAbbott... I tried your answer... It all works for the node part... but I don't see any leafs.. – EagleFox Dec 26 '12 at 22:05
  • Are you sure that the parent nodes are there before the leaf in the data coming back? – CAbbott Dec 26 '12 at 22:06
  • Yes Cabbott... the parent nodes are first and I am getting all the nodes like Parent1, Parent2 and Parent3 but I don't see any leafs – EagleFox Dec 26 '12 at 22:09
  • Hey Cabbott... I got it to work. However it takes forever to get the data on my tree. It crashes, but it works... is there a way to work around that issue – EagleFox Dec 27 '12 at 01:13
  • Thanks CAbbott... I am not sure but I am thinking of trying to use Dictionary might help??? sort of Dictionary... – EagleFox Dec 27 '12 at 14:10
  • Thank you Cabbott... I placed parents and leaves on my method... but my tree does not show anything... am I supposed to create a newList too coz I already have that implemented – EagleFox Dec 27 '12 at 15:56
  • Is my Dictionary defined correctly?? this is how I am doing it...Dictionary> mainList = new List – EagleFox Dec 27 '12 at 16:12
  • Maybe I should modify this to be exactly what you have in the example for class names and properties? Is that what the tree is expecting, specific class and property names? – CAbbott Dec 27 '12 at 16:31
  • I've updated the LINQ example to use data types you had in your question with the same class/variable names – CAbbott Dec 27 '12 at 18:28
  • Thank you CAbbott... your answer really helped... Part of the problem was on my extjs tree rendering too... Thanks a bunch again – EagleFox Dec 28 '12 at 20:58
2

Your best bet would probably be Linq - I created a quick and dirty VB.Net solution to move you in the right direction - The main part being the SECOND Linq statement...

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim x As New List(Of City)

    x.Add(New City With {.ListA = "NY", .ListB = "", .Leaf = "Parent1"})
    x.Add(New City With {.ListA = "", .ListB = "NY", .Leaf = "Leaf1"})
    x.Add(New City With {.ListA = "HI", .ListB = "", .Leaf = "Parent2"})
    x.Add(New City With {.ListA = "AK", .ListB = "", .Leaf = "Parent3"})

    tv1.Nodes.AddRange((From y In x Where y.ListA <> "" Select New TreeNode With {
                                                                    .Name = y.ListA,
                                                                    .Text = y.Leaf}).ToArray)

    For Each nd As TreeNode In tv1.Nodes
        Dim Nm As String = nd.Name
        nd.Nodes.AddRange((From y In x Where y.ListB = Nm Select New TreeNode(y.Leaf)).ToArray)
    Next
End Sub

Simply put, you populate the first ste of parent nodes, then you just loop through all the nodes and populate them with any nodes where ListB = Node's name.

Hope this helps move you in the right direction

John Bustos
  • 19,036
  • 17
  • 89
  • 151
  • Thank you Jonn... but I am totally using c# and besides, I have about 500 data and 30 parent nodes... :) – EagleFox Dec 26 '12 at 22:11
  • 2
    ... I added a comment to your original question with another thought, but my main point here wasn't the code - that's just how i tested it to make sure it worked - It was to populate the first (parent) nodes and then to populate the second using Linq based upon parent name. – John Bustos Dec 26 '12 at 22:15
1

... I'm trying to write this in C#, but this should do it:

        int count = 1;

        List<TreeNode> myNode = new List<TreeNode>();
        foreach (City MyData in giveData)
        {
            // 1st foreach
            if (MyData.ListA != "")
            {

                TreeNode treeNode = new TreeNode();
                treeNode.id = count++;
                treeNode.name = MyData.labelName;
                treeNode.leaf = false;

                foreach (City labelName in giveData)
                {
                    if (MyData.ListA == labelName.ListB)
                    {// 2nd foreach
                        TreeNode node1 = new TreeNode();
                        node1.id = count++;
                        node1.name = labelName.labelName;
                        node1.leaf = true;

                        treeNode.Nodes.Add(node1);
                    }
                }

                myNode.Add(treeNode);
            }
        }
        return JsonConvert.SerializeObject(myNode);

Hope this at least moves you in the right direction...

John Bustos
  • 19,036
  • 17
  • 89
  • 151
  • Hi John... on treeNode.Nodes.Add(node1); I am getting an error on 'Nodes'... it says it does not contain a definition – EagleFox Dec 27 '12 at 14:08
  • I atleast got it to run John... I think u probably meant treeNode.children.Add(node1);... However this takes a lot of time to get my data and causes it to crash... Is there a way to do this more efficiently? I am currently trying to use dictionary but no luck – EagleFox Dec 27 '12 at 17:12