使用 LINQ 呈现层次结构?

发布于 2024-09-20 00:33:18 字数 379 浏览 7 评论 0原文

假设我们有一个类

Category
{
   ID,
   Name,
   ParentID
}

和一个列表,

1, 'Item 1', 0
2, 'Item 2', 0
3, 'Item 3', 0
4, 'Item 1.1', 1
5, 'Item 3.1', 3
6, 'Item 1.1.1', 4
7, 'Item 2.1', 2

我们可以使用 LINQ 来渲染一棵树吗:

Item 1
 Item 1.1
  Item 1.1.1
Item 2
 Item 2.1
Item 3
 Item 3.1

如有帮助,我们将不胜感激!

Let say we have a class

Category
{
   ID,
   Name,
   ParentID
}

and a List

1, 'Item 1', 0
2, 'Item 2', 0
3, 'Item 3', 0
4, 'Item 1.1', 1
5, 'Item 3.1', 3
6, 'Item 1.1.1', 4
7, 'Item 2.1', 2

Can we using LINQ to render a tree like:

Item 1
 Item 1.1
  Item 1.1.1
Item 2
 Item 2.1
Item 3
 Item 3.1

Any help is appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

谁对谁错谁最难过 2024-09-27 00:33:18

这是“仅 LINQ”版本:

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

是的,它是递归 LINQ。


根据 NVA 的请求,以下是使所有“孤立”记录成为根记录的方法:

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

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);
尴尬癌患者 2024-09-27 00:33:18

这些扩展方法正是您想要的:

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

}

您可以按如下方式使用它们:

categories.ByHierarchy(
        cat => cat.ParentId == null, // assuming ParentId is Nullable<int>
        (parent, child) => parent.Id == child.ParentId)
     .DumpHierarchy(cat => cat.Name);

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);
有深☉意 2024-09-27 00:33:18

您可以使用递归:

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

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);                    
            }
        }
    }        
}
段念尘 2024-09-27 00:33:18
 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;
    }
 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;
    }
倚栏听风 2024-09-27 00:33:18
   @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>
   @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>
木緿 2024-09-27 00:33:18
       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;
    }

//调用方法

List<Channel> rootChannel = listChannel.BuildTreeView(f => f.PARENT_CODE, x => x.CODE, z => z.SubChannels, c => c.CODE == "AC");
       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");
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文