层次结构列表的不同输出子级和父级

时间:2017-07-24 03:20:33

标签: c# list hierarchy listitem

我有这样的表层次结构列表:

ID  ParentID
1   0
2   7
3   1
4   5
5   1
6   2
7   1
8   6
9   0
10  9

我希望输出到 listTtem 如果子lvl1添加|___然后+值,如果子lvl 2添加<space>然后|___然后+值< / p>

输出:

1 
|___ 3
|___ 5
     |___ 4
|___ 7 
     |___ 2 
          |___ 6 
               |___ 8
     |___ 11
9
|___ 10

我做了什么:

public class MyClass
{
    public List<MyClass> Children = new List<MyClass>();
    public string ID { get; set; }
    public string ParentID { get; set; }
    public string Name { get; set; }
}

// GET: Reporting
public ActionResult Index()
{
    List<MyClass> ofitems = new List<MyClass>();
    var dbs = db.ORG_FUNCTION.Select(pk => new { pk.FUNCTION_ID, pk.FUNCTION_PARENT_ID, pk.NAME });
    foreach (var s in dbs)
    {
        ofitems.Add(new MyClass { ID = s.FUNCTION_ID.ToString(), ParentID = s.FUNCTION_PARENT_ID.ToString(), Name = s.NAME });
    }
    Action<MyClass> SetChildren = null;
    SetChildren = parent =>
    {
        parent.Children = ofitems
            .Where(childItem => childItem.ParentID == parent.ID)
            .ToList();
        //Recursively call the SetChildren method for each child.
        parent.Children
            .ForEach(SetChildren);
    };
   //ViewBag.list = ????;
    return View();
}

但它只显示相同的输出。如何通过父子获得不同的输出

2 个答案:

答案 0 :(得分:1)

您需要递归遍历树以正确输出它 你的算法确实使用递归,但它只填充Children属性,甚至可以线性完成:

var byNodeId = ofitems.ToDictionary(x => x.ID, x => x);
foreach (var node in ofitems)
{
    // add current node to its parent's Children
    byNodeId[node.ParentID].Children.Add(node); 
}

之所以重要,是因为您的算法需要O(n*n)次操作来填充Children,这比{1}}更糟糕。

即使在填写n之后,您仍然需要通过树DFS (Depth-First Search) - 可以使用Children或递归来完成。
这就是递归方式的样子:

Queue

结果:

LINQPad demo

您的控制器可能如下所示:

public static class TreeFormatter
{
    private static string FormatTreeItem(MyClass item, int depth)
    {
        if (depth == 0) 
            return $"{item.ID}"; // you can use Name in return

        return $"{new string(' ', (depth - 1) * 8)}|___ {item.ID}"; // 8 spaces per level       
    }

    private static void FormatSubtree(StringBuilder sb, List<MyClass> items, string current, int depth)
    {
        foreach (var child in items.Where(x => x.ParentID == current)) // perhaps .OrderBy?
        {
            sb.AppendLine(FormatTreeItem(child, depth));
            FormatSubtree(sb, items, child.ID, depth + 1);
        }
    }

    public static string Format(List<MyClass> items)
    {
        var sb = new StringBuilder();
        FormatSubtree(sb, items, "0", 0); // "0" = root
        return sb.ToString();
    }
}

此代码中有许多地方可以改进它,它只是显示了这个想法。

答案 1 :(得分:0)

构建MyClass树很容易。

试试这个:

ILookup<int, int> lookup = source.ToLookup(x => x.ParentID, x => x.ID);
Func<int, List<MyClass>> build = null;
build = pid =>
    lookup[pid]
        .Select(id => new MyClass()
        {
            Children = build(id),
            ID = id,
            ParentID = pid
        })
        .ToList();

List<MyClass> ofitems = build(0);

如果您从这些数据开始:

var source = new[]
{
    new { ID = 1, ParentID = 0 },
    new { ID = 2, ParentID = 7 },
    new { ID = 3, ParentID = 1 },
    new { ID = 4, ParentID = 5 },
    new { ID = 5, ParentID = 1 },
    new { ID = 6, ParentID = 2 },
    new { ID = 7, ParentID = 1 },
    new { ID = 8, ParentID = 6 },
    new { ID = 9, ParentID = 0 },
    new { ID = 10, ParentID = 9 },
};

然后你得到这个结果:

result

我确实将MyClass的定义更改为使用int作为ID类型而不是字符串,但它也适用于字符串。

输出也相当容易。

试试这个:

string divider = "|___";
Func<IEnumerable<MyClass>, int, IEnumerable<string>> output = null;
output = (items, n) =>
    items
        .SelectMany(item =>
            new []
            {
                "".PadLeft(
                    divider.Length * (n - 1 > 0 ? n - 1 : 0))
                    + (n > 0 ? divider : "")
                    + item.ID
            }.Concat(output(item.Children, n + 1)));

然后用output(ofitems, 0)得到这个:

1 
|___3 
|___5 
    |___4 
|___7 
    |___2 
        |___6 
            |___8 
9 
|___10