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

本贴最后更新于 1663 天前,其中的信息可能已经事过景迁

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

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • FlowUs

    FlowUs.息流 个人及团队的新一代生产力工具。

    让复杂的信息管理更轻松、自由、充满创意。

    1 引用 • 3 关注
  • Gzip

    gzip (GNU zip)是 GNU 自由软件的文件压缩程序。我们在 Linux 中经常会用到后缀为 .gz 的文件,它们就是 Gzip 格式的。现今已经成为互联网上使用非常普遍的一种数据压缩格式,或者说一种文件格式。

    9 引用 • 12 回帖 • 165 关注
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    179 引用 • 407 回帖 • 484 关注
  • 持续集成

    持续集成(Continuous Integration)是一种软件开发实践,即团队开发成员经常集成他们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

    15 引用 • 7 回帖
  • 快应用

    快应用 是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放。

    15 引用 • 127 回帖 • 1 关注
  • GitHub

    GitHub 于 2008 年上线,目前,除了 Git 代码仓库托管及基本的 Web 管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。正因为这些功能所提供的便利,又经过长期的积累,GitHub 的用户活跃度很高,在开源世界里享有深远的声望,并形成了社交化编程文化(Social Coding)。

    211 引用 • 2040 回帖
  • Q&A

    提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。

    8994 引用 • 40975 回帖 • 129 关注
  • TensorFlow

    TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。

    20 引用 • 19 回帖 • 2 关注
  • Chrome

    Chrome 又称 Google 浏览器,是一个由谷歌公司开发的网页浏览器。该浏览器是基于其他开源软件所编写,包括 WebKit,目标是提升稳定性、速度和安全性,并创造出简单且有效率的使用者界面。

    62 引用 • 289 回帖
  • jsoup

    jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。

    6 引用 • 1 回帖 • 493 关注
  • OkHttp

    OkHttp 是一款 HTTP & HTTP/2 客户端库,专为 Android 和 Java 应用打造。

    16 引用 • 6 回帖 • 80 关注
  • GraphQL

    GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑。

    4 引用 • 3 回帖 • 10 关注
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    245 引用 • 1338 回帖 • 1 关注
  • ReactiveX

    ReactiveX 是一个专注于异步编程与控制可观察数据(或者事件)流的 API。它组合了观察者模式,迭代器模式和函数式编程的优秀思想。

    1 引用 • 2 回帖 • 165 关注
  • AngularJS

    AngularJS 诞生于 2009 年,由 Misko Hevery 等人创建,后为 Google 所收购。是一款优秀的前端 JS 框架,已经被用于 Google 的多款产品当中。AngularJS 有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、语义化标签、依赖注入等。2.0 版本后已经改名为 Angular。

    12 引用 • 50 回帖 • 482 关注
  • 宕机

    宕机,多指一些网站、游戏、网络应用等服务器一种区别于正常运行的状态,也叫“Down 机”、“当机”或“死机”。宕机状态不仅仅是指服务器“挂掉了”、“死机了”状态,也包括服务器假死、停用、关闭等一些原因而导致出现的不能够正常运行的状态。

    13 引用 • 82 回帖 • 68 关注
  • Kubernetes

    Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。

    111 引用 • 54 回帖 • 1 关注
  • Vim

    Vim 是类 UNIX 系统文本编辑器 Vi 的加强版本,加入了更多特性来帮助编辑源代码。Vim 的部分增强功能包括文件比较(vimdiff)、语法高亮、全面的帮助系统、本地脚本(Vimscript)和便于选择的可视化模式。

    29 引用 • 66 回帖 • 1 关注
  • RemNote
    2 引用 • 16 回帖 • 9 关注
  • Elasticsearch

    Elasticsearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于 RESTful 接口。Elasticsearch 是用 Java 开发的,并作为 Apache 许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

    117 引用 • 99 回帖 • 215 关注
  • HTML

    HTML5 是 HTML 下一个的主要修订版本,现在仍处于发展阶段。广义论及 HTML5 时,实际指的是包括 HTML、CSS 和 JavaScript 在内的一套技术组合。

    107 引用 • 295 回帖 • 1 关注
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 682 关注
  • Sandbox

    如果帖子标签含有 Sandbox ,则该帖子会被视为“测试帖”,主要用于测试社区功能,排查 bug 等,该标签下内容不定期进行清理。

    420 引用 • 1247 回帖 • 589 关注
  • IBM

    IBM(国际商业机器公司)或万国商业机器公司,简称 IBM(International Business Machines Corporation),总公司在纽约州阿蒙克市。1911 年托马斯·沃森创立于美国,是全球最大的信息技术和业务解决方案公司,拥有全球雇员 30 多万人,业务遍及 160 多个国家和地区。

    17 引用 • 53 回帖 • 148 关注
  • 运维

    互联网运维工作,以服务为中心,以稳定、安全、高效为三个基本点,确保公司的互联网业务能够 7×24 小时为用户提供高质量的服务。

    150 引用 • 257 回帖
  • frp

    frp 是一个可用于内网穿透的高性能的反向代理应用,支持 TCP、UDP、 HTTP 和 HTTPS 协议。

    20 引用 • 7 回帖 • 1 关注
  • 人工智能

    人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。

    142 引用 • 209 回帖