1

I'm looking for the simplest method to store parent child data in memory for manipulation. There will be approx 100 parents, 20 children and 30 grand-children and only the number of parents will change, all child and grand-child properties will be known before compiling. I would like to access the grand children data using the parent ID and property name as a key. I could use a simple class with 200 odd properties, but I'm sure there's a more efficient method.

I've come across nested dictionaries/classes/structures/tuples, lists of lists, lists of arrays, json, non-binary trees etc but most posts are several years old and I'm not sure which method is best.

Please can anyone suggest which method to use or if there's a good library available?

    ID (1 to 100)
        |
        param 1   param 2   param 3  param 4.....param 20
        -val 1    -val 1   -val 1    -val 1      -val 1
        -val 2    -val 2   -val 2    -val 2      -val 2
        -val 3    -val 3   -val 3    -val 3      -val 3
        ...
        -val 30    -val 30   -val 30    -val 30      -val 30
Community
  • 1
  • 1
Zeus
  • 1,496
  • 2
  • 24
  • 53
  • I kind of feel your over thinking it slightly. In memory access of that small number of items is going to be pretty fast whatever. It sounds like you just want a dictionary (key: parent ID and property name, value: child). That said this question is somewhat vague and difficult to follow. If in doubt, what's the simpliest solution. Efficency should be your second thought, don't prematurely optimise your code. – Liam Aug 04 '16 at 12:18
  • [*We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%*](http://programmers.stackexchange.com/questions/80084/is-premature-optimization-really-the-root-of-all-evil) I'd say this lies within the 97% – Liam Aug 04 '16 at 12:22
  • @Liam - I agree with your comment, I guess I wasn't clear enough. For each value the code was reading/writing to a database which has several linked datatables. The method was simple to understand, but ran really slowly as it took a long time to instantiate a new context each time and in hindsight this is obviously wasn't the best method. To speed up the process v2 will read the database once, process the results and then write them back to the database. – Zeus Aug 04 '16 at 23:20

2 Answers2

2

I'd start with a simple, standard approach: define a class for a tree structure and use a list collection for the children nodes and a convenient contructor to assign a parent.

 public class Node
 {
     public List<Node> children = new List<Node>();

     public string Name;
     public int? Val;

     public Node Parent = null;

     public Node(string name, int? val, Node fromParent = null) {
         Name = name;
         Val = val;
         if (fromParent != null)
         {
             Parent = fromParent;
             fromParent.children.Add(this);
         }
     }

 }

A trivial usage:

var parent1 = new Node("parent1",10);
var child1 = new Node("child", 30, parent1);

Inheritance example

Subclasses

public class Root : Node
{
    public int ParentID;
    public Root(int id) : base(null, null, null)
    {
        ParentID = id;
    }
}

public class Parameter : Node
{
    public string ParameterType;
    public Parameter(string paramName, string paramType, Node fromParent = null) : base(paramName, null, fromParent)
    {
        ParameterType = paramType;
    }
}
public class Value : Node
{
    public string Unit;
    public Value(int val, string unit, Node fromParent = null) : base(null, val, fromParent)
    {
        Unit = unit;
    }
}

and how to instantiate them (with random values)

List<Node> parents = new List<Node>();
Random rnd = new Random();
for (int k = 0; k < 100; k++)
{
    var parentk = new Root(k);
    for (int kk = 0; kk < 20; kk++)
    {
        var paramkk = new Parameter("simple" + kk, "param1", parentk);
        for (int kkk = 0; kkk < 30; kkk++)
        {
            int dice = rnd.Next(1, 1000000);
            var valkkk = new Value(dice, "bar", paramkk);
        }

    }
    parents.Add(parentk);
}

in-memory searches

Let's say you have the above collection of parents and you want to find the one with max value for param1

DateTime start = DateTime.Now;
var mySearch = parents.SelectMany(x => x.children)
    .Where(x => ((Parameter)x).ParameterType == "param1")
  .GroupBy(x => x.Parent,
    x => x.children,
    (key, gr) =>
    new
    {
        Key = key,
        MaxVal = gr.Max(y => y.Max(z => z.Val))
    }).OrderByDescending(x => x.MaxVal).First();
var foundID = ((Root)mySearch.Key).ParentID;
DateTime stop  = DateTime.Now;

var elapsed = (stop - start).TotalMilliseconds;

and this search took about 15 milliseconds on my laptop

  • I think the Root constructor should be ``public Root(int id) : base(null, null, null)`` instead of ``public Root() : base(null, null, null)`` ? – Zeus Aug 05 '16 at 01:25
  • No problem. How would I do a search for one of the children's value? say try to find value of k=15, kk=12, kkk=23? – Zeus Aug 05 '16 at 05:13
  • I'm on my mobile at the moment, but does this compile? parents[15].children[12].children[23] or maybe better children.FirstOrDefault(x => x.Val == 23) and so on... –  Aug 05 '16 at 05:38
  • thanks, this works: ``var val = parents[15].children[15].children[5].Val;`` – Zeus Aug 05 '16 at 06:08
  • how would I sum all parents with ID=5 and children with ID=12? something like this? ``var total = parents.SelectMany(x => x.children).Where(x => parents[5] && children[12]).Sum();`` – Zeus Aug 05 '16 at 06:09
  • is there anyway of changing the class so to make the lookup ``parent[x].children[y].grandchild[z]`` or is that just how the tree works? – Zeus Aug 05 '16 at 06:12
  • Ah ... ok, it is just semantic but I assume we can override children into grandchildren for the second (parameter) level. I'll try asap with a compiler... half an hour pls –  Aug 05 '16 at 06:23
  • I mean something like public List grandchild = children; in the Parameter class or an equivalent getter –  Aug 05 '16 at 06:27
  • sounds good. Getting the total for a parent-child-grand-child would also be very useful – Zeus Aug 05 '16 at 06:31
  • well :-) and also the total should be easy with parents[k].SelectMany(x => x.children).SelectMany(x => x.grandchild).Sum(x => x.Val) but I'm still on mobile –  Aug 05 '16 at 06:35
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/120201/discussion-between-zeus-and-machine-learning). – Zeus Aug 05 '16 at 06:38
  • one more question...how can I sum using the parameter type? this doesn't compile ``var sum3 = parents[5].children.SelectMany(x => (((Parameter)x).ParameterType == "param1").grandchild).Sum(x => x.Val);`` – Zeus Aug 05 '16 at 09:57
  • Do you mean `var sum3 = parents[5].children.Where(x => ((Parameter)x).ParameterType == "param1").SelectMany(x => ((Parameter)x).grandchild).Sum(x => x.Val);`? –  Aug 05 '16 at 10:05
  • Machine Learning - I'm trying to get my head around linked classes and how to extract the class properties. Please can you suggest a book, tutorial, video etc where I can learn more about using classes to construct trees? – Zeus Sep 13 '16 at 06:59
  • @Zeus Look for Linq tutorials and walkthroughs: this one is from [msdn](https://msdn.microsoft.com/en-us/library/bb397900.aspx). Ask a new question for more help. –  Sep 13 '16 at 09:48
  • Thanks, I'm fine with Linq, but need some more information on how to access the properties of linked classes not using linq if possible. Is there a process or technique I can search for? I'll ask another question if I can't figure it out. – Zeus Sep 13 '16 at 10:54
2

For this I would use an XML tree.

There are two options:

XDocument is much simpler than using the other method. You can see the difference in this answer.

Here is a sample using XDocument:

var data =
    new XElement("root",
        new XElement("parent",
            new XElement("child",
                new XElement("grandchild"),
                new XElement("grandchild"),
                new XElement("grandchild"),
                //optionally you can add attributes such as Age or ID
                new XElement("grandchild", 
                    new XAttribute("Age","16"), new XAttribute("ID", "47"))
            )
        )
    );

You can than save and open it as an XML file like this:

var filePath = @"C:\XDocumentTest.xml";

using (var fileStream = new FileStream(filePath, FileMode.Create))
using (var writer = new StreamWriter(fileStream))
{
    writer.Write(data);
}
System.Diagnostics.Process.Start(filePath);

This is how the file would look like:

<root>
  <parent>
    <child>
      <grandchild />
      <grandchild />
      <grandchild />
      <grandchild Age="16" ID="47" />
    </child>
  </parent>
</root>
Community
  • 1
  • 1
Jakub Loksa
  • 537
  • 1
  • 14
  • 32
  • thanks for the answer, but I think I'll go for the tree method. – Zeus Aug 05 '16 at 01:37
  • 1
    Plus one for an answer that doesn't reinvent the wheel - something that I see happen far too often. Why write and debug a new library when XML supports everything you want to accomplish? – Mr Anderson Aug 05 '16 at 05:02
  • Sorry for writing here a temporary comment, I've nothing against this answer but let me clarify that writing the classes for the domain model is not reinventing the wheel... or at least it is similar to defining XML schemas –  Aug 05 '16 at 06:18