0

I am attempting to write a basic HTML unordered list from hierarchical data. Here's what the data looks like:

    public class Task
    {
        public int Id { get; set; }
        public int? ParentId { get; set; }
        public string Title { get; set; }
        public Collection<Task> Tasks = new Collection<Task>( );
    }
    
    List<Task> tasks = new List<Task>( );
    
    tasks.Add( new Task { Id = 1, ParentId = null, Title = "Top task" } );
    tasks.Add( new Task { Id = 2, ParentId = 1, Title = "Subtask 1" } );
    tasks.Add( new Task { Id = 3, ParentId = 2, Title = "Subtask 2" } );
    tasks.Add( new Task { Id = 4, ParentId = 1, Title = "Subtask 3" } );
    tasks.Add( new Task { Id = 5, ParentId = 4, Title = "Subtask 4" } );

Here's what I seek to create from the above data:

<ul>
    <li>Top task
    <ul>
        <li>Subtask 1
            <ul>
                <li>Subtask 2</li>
            </ul>
        </li>
    </ul>
        <ul>
            <li>Subtask 3
                <ul>
                    <li>Subtask 4</li>
                </ul>
            </li>
        </ul>
    </li>
</ul>

I have tried the following posts with some success:

Return unordered list from hierarchical sql data and C# Create HTML unordered list from List using Recursion

Here is the code I have adapted from the first link above, and it mostly works, but if the order of the hierarchical data is changed, the method chokes on the first item that is not a child of the previous parent. For example, if I change the order of the data list as follows, it will skip Subtask 1 and 2:

    tasks.Add( new Task { Id = 2, ParentId = 1, Title = "Subtask 1" } );
    tasks.Add( new Task { Id = 1, ParentId = null, Title = "Top task" } );
    tasks.Add( new Task { Id = 3, ParentId = 2, Title = "Subtask 2" } );
    tasks.Add( new Task { Id = 4, ParentId = 1, Title = "Subtask 3" } );
    tasks.Add( new Task { Id = 5, ParentId = 4, Title = "Subtask 4" } );
        public class Task
        {
            public int Id { get; set; }
            public int? ParentId { get; set; }
            public string Title { get; set; }
            public Collection<Task> Tasks = new Collection<Task>( );

            public Task FindTask( int id )
            {
                return FindTask( this, id );
            }

            private Task FindTask( Task task, int id )
            {
                if( task.Id == id )
                {
                    return task;
                }
                Task returnTask = null;
                foreach( Task child in task.Tasks )
                {
                    returnTask = child.FindTask( id );
                    if( returnTask != null )
                    {
                        break;
                    }
                }
                return returnTask;
            }
        }

        List<Task> topTasks = new List<Task>( );

        protected void GetHierarchy( int id )
        {
            List<Task> data = new List<Task>( );

            data.Add( new Task { Id = 1, ParentId = null, Title = "Top task" } );
            data.Add( new Task { Id = 2, ParentId = 1, Title = "Subtask 1" } );
            data.Add( new Task { Id = 3, ParentId = 2, Title = "Subtask 2" } );
            data.Add( new Task { Id = 4, ParentId = 1, Title = "Subtask 3" } );
            data.Add( new Task { Id = 5, ParentId = 4, Title = "Subtask 4" } );

            foreach( var row in data )
            {
                Task tasks = new Task( );
                tasks.Title = row.Title;
                tasks.Id = row.Id;
                if( row.ParentId == null )
                {
                    topTasks.Add( tasks );
                }
                else
                {
                    int parentId = row.ParentId.Value;
                    foreach( Task topTask in topTasks )
                    {
                        Task parentTask = topTask.FindTask( parentId );
                        if( parentTask != null )
                        {
                            parentTask.Tasks.Add( tasks );
                            break;
                        }
                    }
                }
            }
        }

        protected string Render( )
        {
            var s = new StringBuilder( );

            GetHierarchy( id );

            s.Append( "<ul>" );
            foreach( Task child in topTasks )
            {
                RenderTask( s, child );
            }
            s.Append( "</ul>" );

            return s.ToString( );
        }

        private void RenderTask( StringBuilder s, Task task )
        {
            s.Append( "<li>" );
            s.Append( task.Title );
            if( task.Tasks.Count > 0 )
            {
                s.Append( "<ul>" );
                foreach( Task child in task.Tasks )
                {
                    RenderTask( s, child );
                }
                s.Append( "</ul>" );
            }
            s.Append( "</li>" );
        }

I do not want to use jquery treeview or any other component as this needs to be an independent solution.

Can anyone advise me of a better approach or at the very least help me fix the code that I have implemented above so that the order of the data does not break the recursion?

Thanks in advance.

2 Answers 2

1

In your GetHierarchy function, you're looping through each task in the list sequentially. If the first task in the list has a null parentId then it will be added to the topTasks list.

If, however, the first task has a non-null parentId then it's added to the parent task in topTasks. Note that here the parent task does not yet exist in the topTasks list so the task is not inserted.

I would use a recursive function that first gets the task with a null parent and then uses that task's id to call the function recursively until it reaches the bottom of the tree.

A rough example:

Task addSubLists (List<Task> data, Task parent) {
    foreach(Task task in data) {
        if (task.parentId.Value == parent.Id.Value) {
            parent.Tasks.Add(addSubLists(data, task));
        }
    }
    return parent;
}

You'd pass the top-level task into the function and it would add the all descendants in.

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

3 Comments

Thanks @dyoung for the insight. I can see what the issue is from your post. Any pointers for a recursive solution? Hierarchical logic really tests my brain.
@Scho I've added an example of how it might work. Bear in mind I haven't tested this or anything, it's just a rough idea.
Thanks again @dyoung for your help. I worked with it for hours and failed to get it working. In the end I adapted the other post and got it working.
0

In the end, I adapted the answer from C# Create HTML unordered list from List using Recursion, which works well. Here is the code I have used:

        protected string WriteList( )
        {
            var s = new StringBuilder( );
            List<Task> tasks = new List<Task>( );

            tasks.Add( new Task { Id = 2, ParentId = 1, Title = "Subtask 1" } );
            tasks.Add( new Task { Id = 1, ParentId = null, Title = "Top task" } );
            tasks.Add( new Task { Id = 3, ParentId = 2, Title = "Subtask 2" } );
            tasks.Add( new Task { Id = 4, ParentId = 1, Title = "Subtask 3" } );
            tasks.Add( new Task { Id = 5, ParentId = 4, Title = "Subtask 4" } );


            s.Append( "<ul>" );
            foreach( var task in tasks )
            {
                if( task.ParentId == null )
                {
                    WriteTaskList( tasks, task, s );
                }
            }
            s.Append( "</ul>" );

            return s.ToString( );
        }

        private static void WriteTaskList( List<Task> tasks, Task task, StringBuilder s )
        {
            s.Append( "<li>" + task.Title );

            var subtasks = tasks.Where( p => p.ParentId == task.Id );

            if( subtasks.Count( ) > 0 )
            {
                s.Append( "<ul>" );
                foreach( Task p in subtasks )
                {
                    if( tasks.Count( x => x.ParentId == p.Id ) > 0 )
                        WriteTaskList( tasks, p, s );
                    else
                        s.Append( string.Format( "<li>{0}</li>", p.Title ) );
                }
                s.Append( "</ul>" );
            }

            s.Append( "</li>" );
        }

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.