Linq - 如何循环树数据结构来构建树样式对象

时间:2014-11-19 04:14:59

标签: c# linq linq-to-objects

我有以下SQL表数据:

enter image description here

可视化树看起来应该是这样的:

enter image description here

获取我正在使用的顶级节点:

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; }
}

2 个答案:

答案 0 :(得分:0)

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

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>();
    }
}

然后,您可以创建一个简单的方法,该方法接收父项和所有子项的列表,递归地将子项添加到父项的每个子项,然后将子项添加到父项:

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);
}

答案 1 :(得分:0)

我认为我有一种更简单的方法来构建树。

首先,拥有属性AncestorId&amp;每个节点DescendantId,所以我将结构简化为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()
};

这是我得到的结果:

results