Prashanth
Prashanth

Reputation: 99

nested list to multilevel Dictionary in LINQ

I have the following class

public class TreeNode
{
    public string Id { get; set; }
    public int Data { get; set; }
    public List<TreeNode> Children { get; private set; }


    public TreeNode(string node, int data)
    {
        Id = node;
        Data = data;
        Children = new List<TreeNode>();
    }

     public TreeNode(string node, params TreeNode[] children)
     {
        Id = node;
        Children = new List<TreeNode>(children);
     }

    public override string ToString()
    {
        return Id;
    }
} 

It's n-ary tree in which only the leaf nodes(nodes with no children) will have the data property populated. I want to convert this nested list into multilevel dictionary using LINQ. For example if the tree is as below

TreeNode rootNode =
new TreeNode("node-0",
    new TreeNode("node-1",
        new TreeNode("node-2", 20)),
    new TreeNode("node-3", 19),
    new TreeNode("node-4",
        new TreeNode("node-5", 25),
        new TreeNode("node-6", 40)));

I want the above structure to be converted to Dictionary (note: Not flat Dictionary) as below using LINQ. I need the root dictionary as the result of LINQ. How do i get it done. Kindly help me.

Dictionary<string, object> d4 = new Dictionary<string, object>();
d4.Add("node-5", 25);
d4.Add("node-6", 40);
Dictionary<string, object> d3 = new Dictionary<string, object>();
d3.Add("node-4", d4);
Dictionary<string, object> d2 = new Dictionary<string, object>();
d2.Add("node-2", 20);
Dictionary<string, object> d1 = new Dictionary<string, object>();
d1.Add("node-1", d2);
d1.Add("node-3", 19);
d1.Add("node-4", d4);
Dictionary<string, object> root = new Dictionary<string, object>();
root.Add("node-0", d1);

Upvotes: 1

Views: 999

Answers (2)

Hari Prasad
Hari Prasad

Reputation: 16956

Update :

Since question is not about flattening hierarchy, and user is looking for hierarchical Dictionary for a given an input, I've modified code a bit.

public static void Main()
{
    var rootNode =
        new TreeNode("node-0",
            new TreeNode("node-1",
                new TreeNode("node-2", 20)),
            new TreeNode("node-3", 19),
            new TreeNode("node-4",
                new TreeNode("node-5", 25),
                new TreeNode("node-6", 40)));


    var result = ReadHierarchy(new List<TreeNode> {rootNode});

    foreach (var r in result)
    {
        Console.WriteLine("{0} - {1}", r.Key, r.Value);
    }

    Console.ReadKey();
}

private static Dictionary<string, object> ReadHierarchy(IEnumerable<TreeNode> collection)
{
    return collection.ToDictionary(node => node.Id,
        node => node.Children.Count > 0 ? ReadHierarchy(node.Children) : node.Data as object);
}

Working fiddler example

Below solution flattens hierarchy(Use if required).

Here is simple recursive method which takes List as an input and returns flattened collection..

static List<TreeNode> ReadHierarchy(List<TreeNode> collection) 
{
    return collection.SelectMany(c => ReadHierarchy(c.Children)).Concat(collection).ToList();
} 

You can use simple Linq statement to convert returned list to Dictionary.

TreeNode rootNode =
    new TreeNode("node-0",
                 new TreeNode("node-1",
                              new TreeNode("node-2", 20)),
                 new TreeNode("node-3", 19),
                 new TreeNode("node-4",
                              new TreeNode("node-5", 25),
                              new TreeNode("node-6", 40)));


var result = ReadHierarchy(new List<TreeNode> {rootNode}).ToDictionary(c=>c.Id, c=>c); 

Working Fiddler example

Upvotes: 1

Me.Name
Me.Name

Reputation: 12544

Perhaps it would be better to implement the getvalue logic in the treenode itself, but otherwise using recursion, the following should do the trick:

Func<TreeNode, object> getvalue = null;
getvalue = tn => tn.Children.Count == 0 ? (object)tn.Data : tn.Children.ToDictionary(n => n.Id, getvalue);
var result = getvalue(rootNode) as Dictionary<string, object>;

NB, this assumes the root always contains children, otherwise getvalue would return the 'data' of the root node.

Upvotes: 1

Related Questions