Search code examples
c#csvindentation

read csv file and return indented menu c#



I have to create an indented navigation menu using below data from a .csv file:

ID;MenuName;ParentID;isHidden;LinkURL
1;Company;NULL;False;/company
2;About Us;1;False;/company/aboutus
3;Mission;1;False;/company/mission
4;Team;2;False;/company/aboutus/team
5;Client 2;10;False;/references/client2
6;Client 1;10;False;/references/client1
7;Client 4;10;True;/references/client4
8;Client 5;10;True;/references/client5
10;References;NULL;False;/references

Using this data I have to develop an application that will parse the file and present the content in a console as the example below:

. Company
.... About Us
....... Team
.... Mission
. References
.... Client 1
.... Client 2

Menu items should be indented (depending on the parent), hidden items (isHidden==true) shouldn't be presented and items should be ordered alphabetically. So far I tried:

using (StreamReader sr = new StreamReader(@"file.csv"))
        {
            // Read the stream to a string, and write the string to the console.
            string [] lines = sr.ReadToEnd().Split(/*';', */'\n');                
            for (int i = 1; i < lines.Length; i++)
            {                    
                Console.WriteLine($"String no {i} is : {lines[i-1]}");
            }                
        }

With this i'm getting the lines but I'm stuck after that. I'm new in coding so any help will be appreciated :)


Solution

  • heres some code that should help you get off.

    Working sample:

    https://dotnetfiddle.net/L37Gjr

    It first parses the data to a seperate object. This then gets used to build a m-ary tree, or a hierachical structure of connected nodes. (a node has a reference to 0 or more children).

    https://en.wikipedia.org/wiki/M-ary_tree

    Then tree traversal (use google if you need to know more) is used to insert and print the output, There is still something wrong however. it now uses level order traversal to print, this however comes up with an error:

    Found root:1 - Company
    Found root:10 - References
    -------------------
    1 - Company
        2 - About Us
        3 - Mission
            4 - Team
    10 - References
        6 - Client 1
        5 - Client 2
    

    As you can see, it prints 4 - Team on the wrong level. I'll leave it to you to fix it (because i ran out of time), and if not i hope i gave you plenty ideas to go off and research on your own.

    // sample for https://stackoverflow.com/questions/61395486/read-csv-file-and-return-indented-menu-c-sharp by sommmen
    
    using System;
    using System.Collections;
    using System.Linq;
    using System.Collections.Generic;
    
    public class Program
    {
        public class Node<T>
        {
            public T Data {get;set;}    
            public List<Node<T>> Children { get; set;}
    
            public Node()
            {
                Children = new List<Node<T>>();
            }
    
            // Tree traversal in level order
             public List<Node<T>> LevelOrder()
            {
                List<Node<T>> list = new List<Node<T>>();
                Queue<Node<T>> queue = new Queue<Node<T>>();
                queue.Enqueue(this);
                while(queue.Count != 0)
                {               
                    Node<T> temp = queue.Dequeue();
                    foreach (Node<T> child in temp.Children)
                        queue.Enqueue(child);
                    list.Add(temp);
                }
                return list;
            }
    
            public List<Node<T>> PreOrder()
            {
                List<Node<T>> list = new List<Node<T>>();
                list.Add(this);
                foreach (Node<T> child in Children)
                    list.AddRange(child.PreOrder());
                return list;
            }
    
            public List<Node<T>> PostOrder()
            {
                List<Node<T>> list = new List<Node<T>>();
                foreach (Node<T> child in Children)
                    list.AddRange(child.PreOrder());
                list.Add(this);
                return list;
            }
    
    
        }
    
        public class Entity
        {
            public int id   {get;set;}
            public string menuName {get;set;}
            public int? parentID {get;set;}
            public bool isHidden {get;set;}
            public string linkURL  {get;set;}
        }
    
        public static void Main()
        {
            var data = @"ID;MenuName;ParentID;isHidden;LinkURL
    1;Company;NULL;False;/company
    2;About Us;1;False;/company/aboutus
    3;Mission;1;False;/company/mission
    4;Team;2;False;/company/aboutus/team
    5;Client 2;10;False;/references/client2
    6;Client 1;10;False;/references/client1
    7;Client 4;10;True;/references/client4
    8;Client 5;10;True;/references/client5
    10;References;NULL;False;/references";
    
            var lines = data.Split('\n');
    
            var rootNodes = new List<Node<Entity>>();
            var childItems = new List<Entity>();
    
            // Parse the data to entities
            // Items without a parent are used as rootnodes to build a tree
            foreach(var row in lines.Skip(1))
            {           
                var columns = row.Split(';');
    
                var id       = Convert.ToInt32(columns[0]);
                var menuName = columns[1];
                var parentID = ToNullableInt(columns[2]);
                var isHidden = Convert.ToBoolean(columns[3]);
                var linkURL  = columns[4];
    
                var entity = new Entity()
                                  {
                                    id = id,
                                      menuName = menuName,
                                      parentID = parentID,
                                      isHidden = isHidden,
                                      linkURL = linkURL
                                  };
    
                if(parentID == null)
                {
                    Console.WriteLine("Found root:" + entity.id + " - " + entity.menuName);
    
                    rootNodes.Add(new Node<Entity>()
                                  {
                                      Data = entity
                                  });
                }
                else
                {
                    childItems.Add(entity); 
                }
            }
    
            // Add the childElements to their appropriate rootnode
    
            foreach(var rootNode in rootNodes)
            {
                foreach(var childItem in childItems.OrderBy(a=>a.parentID).ThenBy(b=>b.menuName))
                {
                    var newNode = new Node<Entity>()
                    {
                        Data = childItem
                    };
    
                    Insert(rootNode, newNode);
                }
            }
    
            Console.WriteLine("-------------------");
    
            foreach(var rootNode in rootNodes)
            {
                var indent = 0;
                var previous = rootNode;
                foreach(var node in rootNode.LevelOrder())
                {
                    if(node.Data.isHidden) continue;
    
                    if(previous.Data.parentID != node.Data.parentID)
                        indent++;
    
                    for(var i = 0; i < indent; i++)
                        Console.Write("\t");
    
                    Console.WriteLine(node.Data.id + " - " + node.Data.menuName);
                    previous = node;
                }
            }
        }
    
        public static void Insert(Node<Entity> rootNode, Node<Entity> targetNode)
        {
            foreach(var current in rootNode.LevelOrder())
            {
                if(current.Data.id == targetNode.Data.parentID)
                {
                    current.Children.Add(targetNode);   
                    return;
                }
            }
        }
    
        public static int? ToNullableInt(string s)
        {
            int i;
            if (int.TryParse(s, out i)) return i;
            return null;
        }
    }