0

We have a 3rd party workflow system that contains a hierarchical list of actions which is stored as flat file in the following format:

  • Root
  • Root-Node1
  • Root-Node1-Node1
  • Root-Node1-Node2
  • Root-Node1-Node2-Node1
  • Root-Node1-Node2-Node1-Node1
  • Root-Node1-Node2-Node1-Node2
  • Root-Node2
  • Root-Node2-Node1

etc...

Under each node is a list of properties and the aim of this project is to write a small program that will iterate through each file, build a hierarchy from the Root-Node relationships and then we will able to use this model to run reports on the data.

My attempt so far has been to create a Node class, containing a Child property of type List<Node> which will store the hierarchy

I am struggling to figure out how to determine which Node should be the child of which node. My current process is to capture the previous node name and if the current node name contains the previous node name, it must be a child, but I found this to be flakey when it gets a few nodes deep.

Does anyone have any suggestions for a more reliable way to describe these relationships?

1
  • 1
    Can you always add the highest level nodes first? If so, when you're building the hierarchy, also add each node to a Dictionary<string, Node>. The dictionary key will be like `Root-Node1'. Then, simply remove the last part of the child key and use the resulting key to find its parent in the dictionary. Commented Apr 20, 2018 at 7:53

1 Answer 1

1

Something like this should work:

public class Node
{
    public string Key { get; set; }
    public Node Parent { get; set; }
    public IList<Node> Children { get; set; }
}

private Node LoadAll(string[] keys)
{
    var nodes = new Dictionary<string, Node>(StringComparer.OrdinalIgnoreCase);
    var orphanNodes = new List<Node>();
    Node root = null;
    foreach (var key in keys)
    {
        var node = new Node()
        {
            Key = key
        };
        nodes[key] = node;

        int keySeparator = key.LastIndexOf("-");
        if (keySeparator != -1)
        {
            string parentKey = key.Substring(0, keySeparator);
            if (nodes.TryGetValue(parentKey, out var parentNode))
            {
                if (parentNode.Children == null)
                {
                    parentNode.Children = new List<Node>();
                }
                node.Parent = parentNode;
                parentNode.Children.Add(node);
            }
            else
            {
                orphanNodes.Add(node);
            }
        }
        else if (root != null)
        {
            throw new Exception("Root node already exists.");
        }
        else
        {
            root = node;
        }
    }
    foreach (var orphan in orphanNodes)
    {
        string parentKey = orphan.Key.Substring(0, orphan.Key.LastIndexOf("-"));
        if (nodes.TryGetValue(parentKey, out var parentNode))
        {
            if (parentNode.Children == null)
            {
                parentNode.Children = new List<Node>();
            }
            orphan.Parent = parentNode;
            parentNode.Children.Add(orphan);
        }
        else
        {
            throw new Exception("Nodes without parents found.");
        }
    }
    return root;
}

It maintains a list of all nodes (nodes) with their keys, so that any node can easily be looked up form its key. It also maintains a list of nodes for which we didn't have any parents for on the initial loop run through (orphanNodes - you can remove this if your parent nodes always come before your child nodes).

For each key we:

  1. Create an instance of node
  2. Extract the parent key
  3. If there is no parent key, assume it's the root element
  4. Try to locate the parent node. If it's found, add the current node as a child. If it isn't found, add the current node as an orphan.

In case any parent nodes were loaded after their children, we then do the following for every orphan:

  1. Extract the parent key.
  2. Try to locate the parent node. If it's found, add the current node as a child. If it isn't found, throw an error.

Some of the string manipulation could maybe be improved, but this is a way you could approach the situation.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.