Remove empty nodes in a c# List

1.2k Views Asked by At

I have a C# object which after JSON Serialization becomes something like this:

var tree = [{
Id:1,
text: "Parent 1",
ParentId:0
nodes: [
  {
    Id:2,
    text: "Child 1",
    ParentId:1
    nodes: [
      {
        Id:3
        text: "Grandchild 1",
        ParentId:2,
        nodes:[]
      },
      {
        Id:4,
        text: "Grandchild 2",
        ParentId:2,
        nodes: []
      }
    ]
  },
  {
    Id:5
    text: "Child 2",
    ParentId:1,
    nodes: []
  }
]
},
{
Id:6,
text: "Parent 2",
ParentId:0
nodes: []
}];

I want to remove all nodes that are empty i.e. [] from the object or just mark them as null , so my final object will look like

var tree = [{
Id:1,
text: "Parent 1",
ParentId:0
nodes: [
  {
    Id:2,
    text: "Child 1",
    ParentId:1
    nodes: [
      {
        Id:3
        text: "Grandchild 1",
        ParentId:2,
        nodes:null
      },
      {
        Id:4,
        text: "Grandchild 2",
        ParentId:2,
        nodes:null
      }
    ]
  },
  {
    Id:5
    text: "Child 2",
    ParentId:1,
    nodes:null
  }
]
},
{
Id:6,
text: "Parent 2",
ParentId:0
nodes:null
}];

The list is dynamic and can have many branches.Thanks. My C# class is this

public class Tree
{
    public int Id { get; set; }
    public string text { get; set; }
    public int ParentId { get; set; }
    public List<Tree> nodes { get; set; }
}

For creating the tree List Object my function :

var treeItems = new List<Tree>(); //Contails Flat Data No tree
treeItems = SomeMethod(); //To populate flat Data
treeItems.ForEach(item => item.nodes = treeItems.Where(child => child.ParentId == item.Id).ToList());

Now I Get the Tree structure in

var tree = treeItems.First();

I need some logic so that it will put all nodes = null in all nested levels using linq preferably. So that I can use it bootstrap-treeview datasource.

3

There are 3 best solutions below

1
On BEST ANSWER
var treeItems = new List<Tree>(); //Contails Flat Data No tree
treeItems = SomeMethod(); //To populate flat Data
treeItems.ForEach(item => item.nodes = treeItems.Where(child => child.ParentId == item.Id).Any()?treeItems.Where(child => child.ParentId == item.Id).ToList():null);
0
On
  class Program
{
    static void Main(string[] args)
    {
        List<Tree> treeItems = SomeMethod(); 

        Console.WriteLine("BEFORE");            
        Write(treeItems.First(), 0);

        Do(treeItems.First());

        Console.WriteLine();
        Console.WriteLine("AFTER");            
        Write(treeItems.First(), 0);

        Console.ReadKey();
    }

    private static void Write(Tree t, int currentLevel)
    {
        string space = " ".PadLeft(currentLevel);            

        Console.WriteLine($"{space}{t.Id} : {t.text} : {t.nodes?.Count.ToString() ?? "NULL"}");

        if (t.nodes == null)
            return;

        foreach (Tree tree in t.nodes)
        {                                              
            Write(tree, currentLevel + 1);
        }
    }


    private static void Do(Tree t)
    {
        foreach (Tree tree in t.nodes)
        {
            Do(tree);
        }

        if (t.nodes.Count == 0)
            t.nodes = null;                          
    }

    private static List<Tree> SomeMethod()
    {
        List<Tree> root = new List<Tree>()
        {
            new Tree() {Id = 1, text = "Root", ParentId = -1, nodes = new List<Tree>()}
        };

        root[0].nodes.Add(new Tree { Id = 4, text = "Level2A", ParentId = 2, nodes = new List<Tree>() });
        root[0].nodes.Add(new Tree { Id = 5, text = "Level2B", ParentId = 2, nodes = new List<Tree>()});

        root[0].nodes[1].nodes.Add(new Tree { Id = 6, text = "Level3A", ParentId = 5, nodes = new List<Tree>() });
        root[0].nodes[1].nodes.Add(new Tree { Id = 7, text = "Level3B", ParentId = 5, nodes = new List<Tree>() });
        root[0].nodes[1].nodes.Add(new Tree { Id = 8, text = "Level3C", ParentId = 5, nodes = new List<Tree>() });

        root[0].nodes[1].nodes[1].nodes.Add(new Tree { Id = 9, text = "Level4A", ParentId = 7, nodes = new List<Tree>() });


        root[0].nodes[1].nodes[1].nodes[0].nodes.Add(new Tree { Id = 10, text = "Level5A", ParentId = 9, nodes = new List<Tree>() });
        root[0].nodes[1].nodes[1].nodes[0].nodes.Add(new Tree { Id = 11, text = "Level5b", ParentId = 9, nodes = new List<Tree>() });

        return root;
    }

    public class Tree
    {
        public int Id { get; set; }
        public string text { get; set; }
        public int ParentId { get; set; }
        public List<Tree> nodes { get; set; }
    }
}

Output

UPDATE If you need to iterate through all list, not only first item use Do2(tree) method in main function.

private static void Do2(List<Tree> t)
    {
        foreach (Tree tree in t)
        {
            if (tree.nodes.Count == 0)
                tree.nodes = null;
            else
                Do2(tree.nodes);
        }            
    }
1
On

You can simply set nodes as null

foreach (var treeItem in treeItems)
{
    if (treeItem.nodes!=null && treeItem.nodes.Count==0)
    {
        treeItem.nodes = null;                    
    }
}

Edit: For doing it recursively for sub levels:

public static class ResursiveLogic
{
    public static List<Tree> RemoveEmptyNodes(this List<Tree> tree)
    {
        if (tree==null)
        {
            return null;
        }
        if (tree.Count == 0)
        {
            return null;
        }

        foreach (var subtree in tree)
        {
            subtree.nodes = subtree.nodes.RemoveEmptyNodes();
        }
        return tree;
    }
}

Call it like below:

treeItems.RemoveEmptyNodes();

This might not be the best solution as recursion is not good for performance. You should check if your JSON serializer can do this for you by implementing custom code.