代码之家  ›  专栏  ›  技术社区  ›  Midhat

使用1 LINQ语句从分层数据填充树

  •  1
  • Midhat  · 技术社区  · 16 年前

    我已经设置了这个编程练习。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication2
    {
    
        class DataObject {
           public int ID { get; set; }
           public int ParentID { get; set; }
           public string Data { get; set; }
           public  DataObject(int id, int pid, string data) { this.ID = id; this.ParentID = pid; this.Data = data; }
        }
    
        class TreeNode {
            public DataObject Data {get;set;}
            public List<DataObject> Children { get; set; }
        }
    
    
        class Program
        {
    
            static void Main(string[] args)
            {
                List<DataObject> data = new List<DataObject>();
                data.Add(new DataObject(1, 0, "Item 1"));
                data.Add(new DataObject(2, 0, "Item 2"));
                data.Add(new DataObject(21, 2, "Item 2.1"));
                data.Add(new DataObject(22, 2, "Item 2.2"));
                data.Add(new DataObject(221, 22, "Item 2.2.1"));
                data.Add(new DataObject(3, 0, "Item 3"));
    
            }
        }
    }
    

    所需的输出是一个包含3个treeNode的列表,其中包含项1、2和3。项2将包含一个包含2个数据对象的列表作为其子成员,以此类推。

    有人能给出一个提示或一个不可能的结果吗?

    2 回复  |  直到 16 年前
        1
  •  1
  •   Gideon Engelberth    16 年前

    LINQ在递归数据结构方面做得不是特别好,但是您可以接近它。

    class TreeNode
    {
        public DataObject Data { get; set; }
        public List<TreeNode> Children { get; private set; }
    
        public TreeNode(DataObject data)
        {
            Data = data;
            Children = new List<TreeNode>();
        }
    
        //name chosen to match XElement method.  I would name this
        //SelfAndDescendants or change the behavior to match the name.
        public IEnumerable<TreeNode> DescendantsAndSelf()
        {
            return (new TreeNode[] { this }).Concat(from c in this.Children
                                                    from sc in c.DescendantsAndSelf()
                                                    select sc);
        }
    }
    

    现在我们可以定义以下方法:

    public static TreeNode BuildTree(IEnumerable<DataObject> items, int rootId)
    {
        var root = new TreeNode(new DataObject(rootId, int.MinValue, "Root"));
        return (from i in items
                let n = new TreeNode(i)
                group n by n.Data.ParentID into nodeGroup
                orderby nodeGroup.Key ascending
                select nodeGroup)
                .Aggregate(root, (parent, childGroup) =>
                {
                    parent.DescendantsAndSelf()
                          .First(n => n.Data.ID == childGroup.Key)
                          .Children.AddRange(childGroup);
                    return parent; 
                });
    }
    

    这种方法会做出一些假设,但应该会让你走上正确的方向。

    • 传入的元素都不是树的根节点,将创建一个根节点作为返回值。
    • 父节点的id总是低于节点的id(这使得 OrderBy + Aggregate
    • 传递给方法的序列中的每个项都将属于根或其他项之一(否则将由 .First() 电话)。
        2
  •  0
  •   Pop Catalin    16 年前

    var roots = from root in data
    where !data.Any(d => root.ParentID == d.ID)
    select new TreeNode 
    {
        Data = root,
        Children = data.Where(d => d.ParentID == root.ID).ToList(),
    }