【问题标题】:Linq - how to loop a tree data structure to build a tree style objectLinq - 如何循环树数据结构以构建树样式对象
【发布时间】:2014-11-19 04:14:59
【问题描述】:

我有以下 SQL 表数据:

可视化树应该如下所示:

要获得我正在使用的最顶级节点:

var parentNodes = data
    .Where(i => i.AncestorId == i.DescedantId &&
                (data.Count(d => d.DescedantId == i.DescedantId) == 1))
    .ToList();

关于如何构建一个循环遍历结构然后构建树形对象的函数的任何线索?

我的树形对象类是:

public class ProfitCenterRoot
{
    public List<ProfitCenterItem> Data { get; set; }
}

public class ProfitCenterItem
{
    public int AncestorId { get; set; } 
    public int DescendantId { get; set; }
    public string Text { get; set; }
    public bool Leaf { get; set; }

    // These are the child items
    public List<ProfitCenterItem> Data { get; set; }
}

【问题讨论】:

  • 我认为你应该在这里使用recusrive method
  • 我该如何实现?

标签: c# linq linq-to-objects


【解决方案1】:

您可以使用递归将子级添加到每个父级。但首先,我会在你的类中添加一个默认构造函数来初始化数据列表:

public class ProfitCenterRoot
{
    public List<ProfitCenterItem> Data { get; set; }

    public ProfitCenterRoot()
    {
        Data = new List<ProfitCenterItem>();
    }
}

public class ProfitCenterItem
{
    // Existing properties here

    public ProfitCenterItem()
    {
        Data = new List<ProfitCenterItem>();
    }
}

然后你可以创建一个简单的方法,它接受一个 Parent 和一个所有孩子的列表,递归地将孩子添加到父母的每个孩子,然后将孩子添加到父母:

public static void AddChildren(ProfitCenterItem parent, 
    IEnumerable<ProfitCenterItem> allChildren )
{
    var children = allChildren
        .Where(child =>
               child.AncestorId == parent.DescendantId &&
               child.AncestorId != child.DescendantId)
        .ToList();

    foreach (var child in children)
    {
        AddChildren(child, allChildren.Except(children));
        parent.Data.Add(child);
    }
}

所以要填充你的对象,你可以这样做:

var parentNodes = data
    .Where(i => i.AncestorId == i.DescendantId &&
                (data.Count(d => d.DescendantId == i.DescendantId) == 1))
    .ToList();

var root = new ProfitCenterRoot();

foreach (var parentNode in parentNodes)
{
    AddChildren(parentNode, data.Except(parentNodes));
    root.Data.Add(parentNode);
}

【讨论】:

  • 更新:我对AddChildren 方法做了些许性能改进。
【解决方案2】:

我想我有一个更简单的方法来构建树。

首先,每个节点都有属性AncestorIdDescendantId 似乎很奇怪,所以我将结构简化为Id,如下所示:

public class ProfitCenterItem
{
    public int Id { get; set; } 
    public List<ProfitCenterItem> Data { get; set; }
}

现在变得容易了。

实际相关的节点是AncestorId != DescendantId 所在的节点,因此我们得到了这些节点:

var nonSelf =
    data
        .Where(x => x.AncestorId != x.DescendantId)
        .ToArray();

现在,父节点只是那些出现在左侧但不出现在右侧的 id。像这样:

var parentNodes =
    Enumerable
        .Except(
            nonSelf.Select(x => x.AncestorId),
            nonSelf.Select(x => x.DescendantId));

现在要构建树,我们为孩子创建一个查找:

var lookup = nonSelf.ToLookup(x => x.AncestorId, x => x.DescendantId);

这是最棘手的部分 - 用于遍历查找以构建所有节点的递归构建函数:

Func<int, ProfitCenterItem> build = null;
build = n =>
    new ProfitCenterItem()
    {
        Id = n,
        Data = lookup[n].Select(x => build(x)).ToList()
    };

最后我们只需要创建根节点并调用build创建父节点:

var root = new ProfitCenterRoot()
{
    Data = parentNodes.Select(x => build(x)).ToList()
};

这是我得到的结果:

【讨论】:

    猜你喜欢
    • 2019-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多