Rendering a hierarchy using LINQ?

前端 未结 6 1012
终归单人心
终归单人心 2020-12-10 08:05

Let say we have a class

Category
{
   ID,
   Name,
   ParentID
}

and a List

1, \'Item 1\', 0
2, \'Item 2\', 0
3, \'Item 3\'         


        
相关标签:
6条回答
  • 2020-12-10 08:39
           public static List<TSource> BuildTreeView<TSource, TKey>(this List<TSource> allItems
            , Func<TSource, TKey> parentSelector, Func<TSource, TKey> childSelector, Expression<Func<TSource, List<TSource>>> childrenPropertySelector
            , Func<TSource, bool> GetRoot, List<TSource> rootList = null)
        {
    
            if (rootList == null)
                rootList = allItems.Where(GetRoot).ToList();
            if (rootList != null && rootList.Count > 0)
            {
                rootList.ForEach(rootItem =>
                {
                    Func<TSource, bool> whereClause = x => childSelector(rootItem).Equals(parentSelector(x));
                    var childrenProperty = (childrenPropertySelector.Body as MemberExpression).Member as System.Reflection.PropertyInfo;
                    var childrenList = allItems.Where(whereClause).ToList();
                    childrenProperty.SetValue(rootItem, childrenList);
    
                    if (childrenList.Count > 0)
                        BuildTreeView(allItems, parentSelector, childSelector, childrenPropertySelector, GetRoot, childrenProperty.GetValue(rootItem) as List<TSource>);
                });
    
            }
            return rootList;
        }
    

    //Call method

    List<Channel> rootChannel = listChannel.BuildTreeView(f => f.PARENT_CODE, x => x.CODE, z => z.SubChannels, c => c.CODE == "AC");
    
    0 讨论(0)
  • 2020-12-10 08:42

    Here's the "LINQ-only" version:

    Func<int, int, string[]> build = null;
    build = (p, n) =>
    {
        return (from x in categories
                where x.ParentID == p
                from y in new[]
                {
                    "".PadLeft(n)+ x.Name
                }.Union(build(x.ID, n + 1))
                select y).ToArray();
    };
    var lines = build(0, 0);
    

    Yes, it's recursive LINQ.


    Per NVA's request, here's the way to make all "orphan" records become root records:

    Func<IEnumerable<int>, int, string[]> build = null;
    build = (ps, n) =>
    {
        return (from x in categories
                where ps.Contains(x.ParentID)
                from y in new[]
        {
            "".PadLeft(n)+ x.Name
        }.Union(build(new [] { x.ID }, n + 1))
                select y).ToArray();
    };
    
    var roots = (from c in categories
                 join p in categories on c.ParentID equals p.ID into gps
                 where !gps.Any()
                 orderby c.ParentID
                 select c.ParentID).Distinct();
    
    var lines = build(roots, 0);
    
    0 讨论(0)
  • 2020-12-10 08:44

    These extension methods do exactly what you want:

    public static partial class LinqExtensions
    {
        public class Node<T>
        {
            internal Node() { }
    
            public int Level { get; internal set; }
            public Node<T> Parent { get; internal set; }
            public T Item { get; internal set; }
            public IList<Node<T>> Children { get; internal set; }
        }
    
        public static IEnumerable<Node<T>> ByHierarchy<T>(
            this IEnumerable<T> source,
            Func<T, bool> startWith, 
            Func<T, T, bool> connectBy)
        {
            return source.ByHierarchy<T>(startWith, connectBy, null);
        }
    
        private static IEnumerable<Node<T>> ByHierarchy<T>(
            this IEnumerable<T> source,
            Func<T, bool> startWith,
            Func<T, T, bool> connectBy,
            Node<T> parent)
        {
            int level = (parent == null ? 0 : parent.Level + 1);
    
            if (source == null)
                throw new ArgumentNullException("source");
    
            if (startWith == null)
                throw new ArgumentNullException("startWith");
    
            if (connectBy == null)
                throw new ArgumentNullException("connectBy");
    
            foreach (T value in from   item in source
                                where  startWith(item)
                                select item)
            {
                var children = new List<Node<T>>();
                Node<T> newNode = new Node<T>
                {
                    Level = level,
                    Parent = parent,
                    Item = value,
                    Children = children.AsReadOnly()
                };
    
                foreach (Node<T> subNode in source.ByHierarchy<T>(possibleSub => connectBy(value, possibleSub),
                                                                  connectBy, newNode))
                {
                    children.Add(subNode);
                }
    
                yield return newNode;
            }
        }
    
        public static void DumpHierarchy<T>(this IEnumerable<Node<T>> nodes, Func<T, string> display)
        {
            DumpHierarchy<T>(nodes, display, 0);
        }
    
        private static void DumpHierarchy<T>(IEnumerable<LinqExtensions.Node<T>> nodes, Func<T, string> display, int level)
        {
            foreach (var node in nodes)
            {
                for (int i = 0; i < level; i++) Console.Write("  ");
                Console.WriteLine (display(node.Item));
                if (node.Children != null)
                    DumpHierarchy(node.Children, display, level + 1);
            }
        }
    
    }
    

    You can use them as follows:

    categories.ByHierarchy(
            cat => cat.ParentId == null, // assuming ParentId is Nullable<int>
            (parent, child) => parent.Id == child.ParentId)
         .DumpHierarchy(cat => cat.Name);
    
    0 讨论(0)
  • 2020-12-10 08:44
       @model List<OrgChart.Models.Node>
       @{    
    
        Func<int?, List<OrgChart.Models.Node>, string> recuresive = null;
    recuresive = (parentid, list) => string.Join("", list.Where(x => x.ParentId == parentid).Select(x => "<li>" + x.Name + "<ul>" + recuresive(x.Id, list.Where(y => y.ParentId != parentid).ToList()) + "</ul></li>"));
       }
      @Html.Raw("<ul id='org1' >" + recuresive(null, Model) + "</ul>")
      <div id="chart" class="orgChart"></div>
    
    0 讨论(0)
  • 2020-12-10 08:47
     public IEnumerable<HelpPageMenuItem> GetHelpPageMenuItems()
        {
            var helpPages = (from h in Context.HelpPages select new HelpPageMenuItem{HelpPageId = h.HelpPageId, ParentHelpPageId = h.ParentHelpPageId, PageContext = h.PageContext, MenuText = h.MenuText}).ToList();
            var parents = from h in helpPages where !h.ParentHelpPageId.HasValue select PopulateChildren(h, helpPages);
            return parents.ToList();
        }
    
        private static HelpPageMenuItem PopulateChildren(HelpPageMenuItem helpPageMenuItem, IEnumerable<HelpPageMenuItem> helpPages)
        {
            helpPageMenuItem.ChildHelpPages =
                (from h in helpPages
                 where h.ParentHelpPageId == helpPageMenuItem.HelpPageId
                 select PopulateChildren(h, helpPages)).ToList();
    
            return helpPageMenuItem;
        }
    
    0 讨论(0)
  • 2020-12-10 08:48

    You can use recursion:

    public class Category
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int ParentID { get; set; }
        public List<Category> Children { get; set; }
    }
    
    class Program
    {
        static void Main()
        {
            List<Category> categories = new List<Category>()
            {
                new Category () { ID = 1, Name = "Item 1", ParentID = 0},
                new Category() { ID = 2, Name = "Item 2", ParentID = 0 },
                new Category() { ID = 3, Name = "Item 3", ParentID = 0 },
                new Category() { ID = 4, Name = "Item 1.1", ParentID = 1 },
                new Category() { ID = 5, Name = "Item 3.1", ParentID = 3 },
                new Category() { ID = 6, Name = "Item 1.1.1", ParentID = 4 },
                new Category() { ID = 7, Name = "Item 2.1", ParentID = 2 }
            };
    
            List<Category> hierarchy = new List<Category>();                        
            hierarchy = categories
                            .Where(c => c.ParentID == 0)
                            .Select(c => new Category() { ID = c.ID, Name = c.Name, ParentID = c.ParentID, Children = GetChildren(categories, c.ID) })
                            .ToList();
    
            HieararchyWalk(hierarchy);            
    
            Console.ReadLine();
        }        
    
        public static List<Category> GetChildren(List<Category> categories, int parentId)
        {            
            return categories
                    .Where(c => c.ParentID == parentId)
                    .Select(c => new Category { ID = c.ID, Name = c.Name, ParentID = c.ParentID, Children = GetChildren(categories, c.ID) })
                    .ToList();
        }
    
        public static void HieararchyWalk(List<Category> hierarchy)
        {
            if (hierarchy != null)
            {
                foreach (var item in hierarchy)
                {
                    Console.WriteLine(string.Format("{0} {1}", item.ID, item.Name));
                    HieararchyWalk(item.Children);                    
                }
            }
        }        
    }
    
    0 讨论(0)
提交回复
热议问题