【问题标题】:Read a generic List<T> with parent/child hierarchy into a datatable preserving the parent/child hierarchy将具有父/子层次结构的通用 List<T> 读入保留父/子层次结构的数据表中
【发布时间】:2011-06-03 10:22:22
【问题描述】:

我已经用单位名称转换了这个 XML 树: 暗淡 = 尺寸

Dim1            
|---MG1     
    |---M1  
    |---M2  
    |---M3  
        |---MG31
        |---MG32


Dim2            
|---MG220       
    |---MG2222  

...进入一个单元列表,也就是列表,其中每个单元都可以有另一个具有无限层次结构的列表。现在我想将列表转换为具有父/子层次结构的表格格式。

这样数据表应该是这样的:

Dimension...Parent..Child
Dim1........Dim1....MG1
Dim1........MG1.....M1
Dim1........MG1.....M2
Dim1........Dim1....MG2
Dim1........MG1.....M3
Dim1........M3......MG31
Dim1........M3......MG32
Dim2........Dim2....MG220
Dim2........MG220...MG2222

public class Unit           
{           
   public String Name { get; set; }         
   public Unit Dimension { get; set; }          
   public UnitParent { get; set; }          
   public List<Unit> Children { get; set; }         

}

问题:您将如何迭代 List 并将所有数据写入 DataTable ? 一定有一个棘手的算法我找不到。

【问题讨论】:

    标签: c# linq recursion tree parent-child


    【解决方案1】:

    在这里递归是正确的方法,但我将建议一种 LINQ 方法 - 特别是因为这个问题是用 LINQ 标记的。

    LINQ 的优点之一是扩展方法可以消除很多复杂性,并留下简单的查询来简单地表达业务逻辑。

    这就是我用来展平递归结构的方法:

    public static IEnumerable<T> Flatten<T>(this Func<T, IEnumerable<T>> @this, T root)
    {
        var head = new [] { root, };
        var tail =
            from c in @this(root)
            where !c.Equals(root)
            from d in @this.Flatten(c)
            select d;
        return head.Concat(tail);
    }
    

    注意对Flatten 的递归调用,这是在从给定父项返回子项的函数上定义的扩展方法。

    现在我们可以像这样定义Func&lt;T, IEnumerable&lt;T&gt;&gt;

    Func<Unit, IEnumerable<Unit>> f = u => u.Children;
    

    然后,假设所有 DimensionParentChildren 属性不为空,我们可以使用此查询生成要添加到表中的记录列表:

    var records =
        from r in dimensions
        from d in f.Flatten(r)
        select new
        {
            Dimension = d.Dimension.Name,
            Parent = d.Parent.Name,
            d.Name,
        };
    

    现在,如果有任何属性为 null,则解决方法如下。

    f重新定义为:

    Func<Unit, IEnumerable<Unit>> f = u => u.Children ?? new List<Unit>();
    

    并添加此扩展方法:

    public static R ValueOrNull<T, R>(this T @this, Func<T, R> selector)
        where R : class
    {
        return @this != null ? selector(@this) : null;
    }
    

    现在查询的工作方式如下:

    var records =
        from r in dimensions
        from d in f.Flatten(r)
        select new
        {
            Dimension = d.Dimension.ValueOrNull(x => x.Name),
            Parent = d.Parent.ValueOrNull(x => x.Name),
            d.Name,
        };
    

    仍然非常相似,但null 安全。

    我希望这会有所帮助。

    【讨论】:

    • 谢谢。我给了你一个很好的 sn-p 点,我肯定可以改天使用。不是一个解决方案,因为我意识到现有的代码结构与我想象/预期的不同,因此它需要另一个非常简单的解决方案:)
    【解决方案2】:

    一个简单的递归算法就可以了...

    function AddSubTree(Unit unit) {
      if (unit != unit.Dimension)
        AddItemToDataTable(unit.Dimension.Name, unit.UnitParent.Name, unit.Name);
      foreach (Unit childUnit in unit.Children) {
        AddSubTree(childUnit); 
      }
    }
    

    您为拥有的每个维度对象调用AddSubTree

    【讨论】:

    • 不起作用。传递给 AddSubTree 的单元通常是 null 抛出异常。实际上一个维度不应该为空......我想如果不在这里抛出所有源代码就无法解决......对不起Variant,但谢谢帮助。我现在必须玩...
    猜你喜欢
    • 1970-01-01
    • 2011-04-23
    • 1970-01-01
    • 2020-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多