简体   繁体   中英

C# How to Get list of Ids from Parent-Child list

I have a list of tasks containing parent-child relation. The relations keep getting changed. As the list structure is flat, i need to keep each task sorted, by DisplayOrder column.

Fields:

TaskID - Title   -    ParentID  -  DisplayOrder
1        A Task       Null      -     1
2        B Task       Null            3
3        C - A        1               2
4        D - B        2               4

What I can imagine that after any relation is changed, I get all the tasks Ids in a way that each child ID is under its own Parent-ID and then update the DisplayOrder in of those IDs in their current sequence.

But I am not sure how this is possible in c#. Please advise in this regard.

You're dealing with a tree structure, so this calls for recursion.

If I understand you correctly, you want to enumerate the tasks in the following way (pseudo code, obviously):

foreach(rootTask in all-tasks-having-no-parent)
{
    yield return rootTask;
    foreach(childTask in rootTask's-direct-children)
    {
        do-the-same-with-childTask-and-its-children-as-I'm-doing-with-rootTask;
    }
}

So let's implement the recursive method:

public static class MyExtensions
{
    /// <summary>
    /// Traverse a hierachy of items depths-first.
    /// </summary>
    /// <param name="source">The item source.</param>
    /// <param name="childrenGetter">Function to get the direct children of an item.</param>
    /// <returns>The items in <paramref name="source"/>, each recursively followed by it's descendants.</returns>
    public static IEnumerable<TSource> DepthFirst<TSource>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TSource>> childrenGetter)
    {
        foreach(var item in source)
        {
            yield return item;
            foreach (var descendant in childrenGetter(item).DepthFirst(childrenGetter))
                yield return descendant;
        }
    }
}

All we need now is to know the root tasks as well as how to get the child tasks for a given task. Both is most easily and efficiently done with a Lookup , which essentially is a dictionary with the value type being an IEnumerable.

var tasks = new[]
{
    new Task { Id = 1, Title = "A", ParentId = null },
    new Task { Id = 2, Title = "B", ParentId = null },
    new Task { Id = 3, Title = "C", ParentId = 1 },
    new Task { Id = 4, Title = "D", ParentId = 2 },
};
var childrenByParentId = tasks.ToLookup(t => t.ParentId);

Now you can enumerate the tasks in the desired order and assign the DisplayOrder :

var order = 0;
foreach (var task in childrenByParentId[null].DepthFirst(parent => childrenByParentId[parent.Id]))
    task.DisplayOrder = ++order;

That looks like is a tree structure. You should mount something like this:

      root 
        |
  ----------------
  |              |
A Task        B Task 
  |              |
C - A         D - B  

After that you should make a depth first search to create the DisplayOrder. Check here: https://en.wikipedia.org/wiki/Depth-first_search

EDIT: The portuguese version has a nice image that helps: https://pt.wikipedia.org/wiki/Busca_em_profundidade#/media/File:Depthfirst.png

Then you will get this:

    root (0)
        |
  ----------------
  |              |
A Task (1)    B Task (3)
  |              |
C - A  (2)    D - B  (4)

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM