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

从父/子对象的平面列表中构建层次结构对象

  •  20
  • gregmac  · 技术社区  · 16 年前

    我有一个层次结构中的项目列表,我正试图将这个列表解析成一个实际的对象层次结构。我正在使用 modified pre-order tree traversal

    • 项目A
      • 项目A.1
      • 项目A.2
        • 项目A.2.2
    • B项
      • 项目B.1

    我得到清单:

    • 项目A、项目A.1、项目A.2、项目A.2.2、项目B、项目B.1、项目C

    我要做的是将其解析为包含树的实际结构的对象,例如:

    Class TreeObject {
        String Name;
        Guid ID; 
        Guid ParentID;
        List<TreeObject> Children;
    }
    

    List<TreeObject> FlatToHeirarchy(List<TreeObject> list); 
    

    它接受平面列表,并返回嵌套列表。

    换言之:

    List<TreeObject> flatSet = LoadTreeObjectsFromDatabase(); 
    // flatSet.count == 7; flatSet(0).Children == null
    List<TreeObject> nestedSet = FlatToHeirarchy(flatSet);
    // nestedSet.count == 3; nestedSet(0).Children.count == 2
    

    我不知道如何做到这一点-跟踪家长,并能够处理更大的跳跃(例如,项目a.2.2->项目B)。


    编辑:我在这里寻找一个非暴力解决方案(例如,不要循环多次,将项目移动到子节点,直到只剩下顶级父节点)。我猜有一种优雅的方法可以循环一次,然后根据需要放置项目。

    5 回复  |  直到 16 年前
        1
  •  35
  •   gregmac    7 年前

    这是我最后写的函数。我使用MPTT来存储对象,所以列表是按“left”值的顺序排列的,这基本上意味着父对象总是位于列表中任何给定项的前面。换句话说,item.ParentID引用的项始终已添加(顶级节点或根节点除外)。

    public class TreeObject
    {
        public int Id { get; set; }
        public int ParentId { get; set; }
        public string Name { get; set; }
        public IList<TreeObject> Children { get; set; } = new List<TreeObject>();
    }
    
    public IEnumerable<TreeObject> FlatToHierarchy(List<TreeObject> list)
    {
        // hashtable lookup that allows us to grab references to containers based on id
        var lookup = new Dictionary<int, TreeObject>();
        // actual nested collection to return
        var nested = new List<TreeObject>();
    
        foreach (TreeObject item in list)
        {
            if (lookup.ContainsKey(item.ParentId))
            {
                // add to the parent's child list 
                lookup[item.ParentId].Children.Add(item);
            }
            else
            {
                // no parent added yet (or this is the first time)
                nested.Add(item);
            }
            lookup.Add(item.Id, item);
        }
    
        return nested;
    }
    

    和一个简单的测试(在LinqPad中工作):

    void Main()
    {
        var list = new List<TreeObject>() {
            new TreeObject() { Id = 1, ParentId = 0, Name = "A" },
            new TreeObject() { Id = 2, ParentId = 1, Name = "A.1" },
            new TreeObject() { Id = 3, ParentId = 1, Name = "A.2" },
            new TreeObject() { Id = 4, ParentId = 3, Name = "A.2.i" },
            new TreeObject() { Id = 5, ParentId = 3, Name = "A.2.ii" }
        };
    
        FlatToHierarchy(list).Dump();
    }
    

    结果:

    enter image description here

    public IList<TreeObject> FlatToHierarchy(IEnumerable<TreeObject> list, int parentId = 0) {
        return (from i in list 
                where i.ParentId == parentId 
                select new TreeObject {
                    Id = i.Id, 
                    ParentId = i.ParentId,
                    Name = i.Name,
                    Children = FlatToHierarchy(list, i.Id)
                }).ToList();
    }
    
        2
  •  3
  •   Coincoin    16 年前

    我想你已经知道所有项目的父项了

    下面是一些伪代码:

    foreach Item item in flatlist
       if item.Parent != null
          Add item to item.Parent.ChildrenList
          Remove item from flatlist
       end if
    end for
    

    这个问题 很难,但事实并非如此。很多人从错误的角度看待这个问题;您不能尝试填充每个子项列表,而是要从平面列表中删除子项,这样就很容易了。

        3
  •  1
  •   Chris Barry    13 年前

    正常编译的替代版本,我不确定上面的代码是否有问题。

    private List<Page> FlatToHierarchy(List<Page> list) {
          // hashtable lookup that allows us to grab references to the parent containers, based on id
            Dictionary<int, Page> lookup = new Dictionary<int, Page>();
          // actual nested collection to return
          List<Page> nested = new List<Page>();
    
          foreach(Page item in list) {
            if (lookup.ContainsKey(item.parentId)) {
              // add to the parent's child list 
              lookup[item.parentId].children.Add(item); //add item to parent's childs list
              lookup.Add(item.pageId, item); //add reference to page in lookup table
            } else {
              // no parent added yet (or this is the first time)
              nested.Add(item);     //add item directly to nested list                
              lookup.Add(item.pageId, item); //add reference to page in lookup table
            }
          }
        return nested;
        }
    
        5
  •  0
  •   Rohan West    16 年前

    下面是一个例子,希望这能有所帮助

    class Program
    {
        static void Main(string[] args)
        {
            TreeObject a  = new TreeObject() { Name = "Item A" };
            a.Children.Add( new TreeObject() { Name = "Item A.1" });
            a.Children.Add( new TreeObject() { Name = "Item A.2" });
    
            TreeObject b = new TreeObject() { Name = "Item B" };
            b.Children.Add(new TreeObject() { Name = "Item B.1" });
            b.Children.Add(new TreeObject() { Name = "Item B.2" });
    
            TreeObject c = new TreeObject() { Name = "Item C" };
    
            List<TreeObject> nodes = new List<TreeObject>(new[] { a, b, c });
    
            string list = BuildList(nodes);
            Console.WriteLine(list); // Item A,Item A.1,Item A.2,Item B,Item B.1,Item B.2,Item C
    
            List<TreeObject> newlist = new List<TreeObject>();
            TreeObject temp = null;
    
            foreach (string s in list.Split(','))
            {
                if (temp == null || !s.Contains(temp.Name) || temp.Name.Length != s.Length)
                {
                    temp = new TreeObject() { Name = s };
                    newlist.Add(temp);
                }
                else
                {
                    temp.Children.Add(new TreeObject() { Name = s });
                }                              
            }
    
            Console.WriteLine(BuildList(newlist)); // Item A,Item A.1,Item A.2,Item B,Item B.1,Item B.2,Item C
        }
    
        static string BuildList(List<TreeObject> nodes)
        {
            StringBuilder output = new StringBuilder();
            BuildList(output, nodes);
            return output.Remove(output.Length - 1, 1).ToString();
        }
    
        static void BuildList(StringBuilder output, List<TreeObject> nodes)
        {
            foreach (var node in nodes)
            {
                output.AppendFormat("{0},", node.Name);
                BuildList(output, node.Children);
            }
        }
    }
    
    public class TreeObject
    {
        private List<TreeObject> _children = new List<TreeObject>();
    
        public string Name { get; set; }
        public Guid Id { get; set; }
        public List<TreeObject> Children { get { return _children; } }
    }
    

    }

        6
  •  0
  •   Paulo Vj    10 年前

    格雷格马克

    IList<TreeObject> FlatToHierarchy(IQueryable<lcc_classe> list, int? parentId)
        {
            var q = (from i in list
                    where i.parent_id == parentId
                    select new
                    {
                        id = i.id,
                        parent_id = i.parent_id,
                        kks = i.kks,
                        nome = i.nome
                    }).ToList();
            return q.Select(x => new TreeObject
                {
                    children = FlatToHierarchy(list, x.id)
                }).ToList();
        }