zoukankan      html  css  js  c++  java
  • 【转】无限极列表转换树结构的通用方法

    原文链接:http://www.loogn.net/list2tree

    参考链接:https://blog.csdn.net/qq_19244927/article/details/106481777

    后台做菜单管理、商品分类管理的时候,我们建表一般会有个ParentId对应父ID,这样能形成无限极的分类。存在数据库中是列表数据,当我们返回前端的时候一般会处理成树形数据,方便前端展示,这个场景比较多,所以可以想到使用泛型来写一个算法模板。

    首先定义数据库模型的接口:

        /// <summary>
        /// 水平对象接口,一般是数据库实体对象
        /// </summary>
        /// <typeparam name="TId"></typeparam>
        public interface ILevelModel<TId> where TId : struct
        {
            TId Id { get; set; }
            TId ParentId { get; set; }
        }
    

    ID的类型一般是int类型,但是也有习惯使用long类型的,所以这里使用TId泛型来表示。

    然后定义返回前端的接口:

        /// <summary>
        /// 有children属性的可内嵌本身的层级对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public interface INestedModel<T>
        {
            ICollection<T> Children { get; set; }
        }
    

    这个接口超级简单,就是一个子集合。

    接下来就可以实现我们的通用方法了:

            /// <summary>
            /// 从水平list转变成嵌套tree列表
            /// </summary>
            /// <param name="targetCollection">目标tree列表</param>
            /// <param name="sourceData">所有源数据</param>
            /// <param name="parentId">第一级的parentId,默认0</param>
            /// <typeparam name="T">目标对象类型</typeparam>
            /// <typeparam name="S">原对象类型</typeparam>
            /// <typeparam name="IId">原对象id类型</typeparam>
            public static void ListToTree<T, S, IId>(ICollection<T> targetCollection, IEnumerable<S> sourceData,
                IId parentId = default(IId)) where T : INestedModel<T> where S : ILevelModel<IId> where IId : struct
    
            {
                foreach (var sysRes in sourceData.Where(x => x.ParentId.Equals(parentId)))
                {
                    var info = SimpleMapper.Map<T>(sysRes);
                    if (sourceData.Any(x => x.ParentId.Equals(sysRes.Id)))
                    {
                        info.Children = new List<T>();
                        ListToTree(info.Children, sourceData, sysRes.Id);
                    }
    
                    targetCollection.Add(info);
                }
            }
    

    这样基本就完成了,但是有可能有这样的情况,现有的类中属性名字不是Id、不是ParentId或者不是Children,这个时候上面的接口定义就不方便了,为了更加通用,我们可以另外增加两个接口,里面只包含方法,不使用属性:

        /// <summary>
        /// 水平对象接口,一般是数据库实体对象,版本2
        /// </summary>
        /// <typeparam name="IId"></typeparam>
        public interface ILevelModel2<out IId> where IId : struct
        {
            IId GetId();
            IId GetParentId();
        }
    
        /// <summary>
        /// 有children属性的可内嵌本身的层级对象,版本2
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public interface INestedModel2<T>
        {
            ICollection<T> GetChildren();
            void SetChildren(ICollection<T> children);
        }
    

    这样就更灵活了,然后在实现一个针对这两个接口的方法:

            /// <summary>
            /// 从水平list转变成嵌套tree列表
            /// </summary>
            /// <param name="targetCollection">目标tree列表</param>
            /// <param name="sourceData">所有源数据</param>
            /// <param name="parentId">第一级的parentId,默认0</param>
            /// <typeparam name="T">目标对象类型</typeparam>
            /// <typeparam name="S">原对象类型</typeparam>
            /// <typeparam name="IId">原对象id类型</typeparam>
            public static void ListToTree2<T, S, IId>(ICollection<T> targetCollection, IEnumerable<S> sourceData,
                IId parentId = default(IId)) where T : INestedModel2<T> where S : ILevelModel2<IId> where IId : struct
    
            {
                foreach (var sysRes in sourceData.Where(x => x.GetParentId().Equals(parentId)))
                {
                    var info = SimpleMapper.Map<T>(sysRes);
                    if (sourceData.Any(x => x.GetParentId().Equals(sysRes.GetId())))
                    {
                        info.SetChildren(new List<T>());
                        ListToTree2(info.GetChildren(), sourceData, sysRes.GetId());
                    }
    
                    targetCollection.Add(info);
                }
            }
    

    调用的例子:

        public class Res : ILevelModel<int>
        {
            public int Id { get; set; }
            public int ParentId { get; set; }
            public string Name { get; set; }
        }
    
        public class ResInfo : INestedModel<ResInfo>
        {
            public string Name { get; set; }
            public int Id { get; set; }
            public ICollection<ResInfo> Children { get; set; }
        }
    
            static void Main(string[] args)
            {
                var sourceData = new List<Res>();
                sourceData.Add(new Res() {Id = 1, Name = "第1个", ParentId = 0});
                sourceData.Add(new Res() {Id = 2, Name = "第2个", ParentId = 0});
                sourceData.Add(new Res() {Id = 3, Name = "第1-1个", ParentId = 1});
                sourceData.Add(new Res() {Id = 4, Name = "第1-2个", ParentId = 1});
                sourceData.Add(new Res() {Id = 5, Name = "第2-1个", ParentId = 2});
                sourceData.Add(new Res() {Id = 6, Name = "第2-1-1个", ParentId = 5});
                sourceData.Add(new Res() {Id = 7, Name = "第2-1-2个", ParentId = 5});
    
    
                var list = new List<ResInfo>();
                CollectionHelper.ListToTree(list, sourceData, 0);
    
                var json = JsonSerializer.Serialize(list, new JsonSerializerOptions()
                {
                    WriteIndented = true,
                    Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
                });
                Console.WriteLine(json);
            }
    

    标题:无限极列表转换树结构的通用方法
    作者:loogn
    地址:http://www.loogn.net/list2tree
  • 相关阅读:
    Mozilla Prism v0.9 For Windows/Linux/Mac
    Firefox 3.0十大年夜新特征(1)
    刊行版:Epidemic GNU/Linux 2.1发布
    斥地版:Red Hat Enterprise Linux 4.7 Beta公布公布
    linux下安装drcom1.3.7心得
    Oracle老手艺对Linux意味着什么?
    学Linux要火山式的驾御还是垂垂来
    Firefox 3.0 RC2本周颁发
    net命令详解 **net accounts /maxpwage:unlimited
    学习官方示例 TApplication.OnDeactivate
  • 原文地址:https://www.cnblogs.com/binzhou75/p/13583722.html
Copyright © 2011-2022 走看看