无限极列表转换树结构的通用方法

念初的小站 小小酱油瓶 本文由博客端 http://www.loogn.net 主动推送

后台做菜单管理、商品分类管理的时候,我们建表一般会有个 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);
        }

赞助商 我要投放

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...