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

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

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

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • Follow
    4 引用 • 12 回帖 • 13 关注
  • Ant-Design

    Ant Design 是服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。

    17 引用 • 23 回帖 • 1 关注
  • DevOps

    DevOps(Development 和 Operations 的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。

    58 引用 • 25 回帖
  • 30Seconds

    📙 前端知识精选集,包含 HTML、CSS、JavaScript、React、Node、安全等方面,每天仅需 30 秒。

    • 精选常见面试题,帮助您准备下一次面试
    • 精选常见交互,帮助您拥有简洁酷炫的站点
    • 精选有用的 React 片段,帮助你获取最佳实践
    • 精选常见代码集,帮助您提高打码效率
    • 整理前端界的最新资讯,邀您一同探索新世界
    488 引用 • 384 回帖 • 8 关注
  • 学习

    “梦想从学习开始,事业从实践起步” —— 习近平

    173 引用 • 518 回帖
  • Quicker

    Quicker 您的指尖工具箱!操作更少,收获更多!

    37 引用 • 157 回帖 • 1 关注
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    107 引用 • 153 回帖 • 1 关注
  • CAP

    CAP 指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

    12 引用 • 5 回帖 • 635 关注
  • Q&A

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

    9645 引用 • 43861 回帖 • 93 关注
  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    180 引用 • 120 回帖
  • OpenResty

    OpenResty 是一个基于 NGINX 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

    17 引用 • 57 关注
  • Google

    Google(Google Inc.,NASDAQ:GOOG)是一家美国上市公司(公有股份公司),于 1998 年 9 月 7 日以私有股份公司的形式创立,设计并管理一个互联网搜索引擎。Google 公司的总部称作“Googleplex”,它位于加利福尼亚山景城。Google 目前被公认为是全球规模最大的搜索引擎,它提供了简单易用的免费服务。不作恶(Don't be evil)是谷歌公司的一项非正式的公司口号。

    49 引用 • 192 回帖
  • CSS

    CSS(Cascading Style Sheet)“层叠样式表”是用于控制网页样式并允许将样式信息与网页内容分离的一种标记性语言。

    199 引用 • 542 回帖
  • Office

    Office 现已更名为 Microsoft 365. Microsoft 365 将高级 Office 应用(如 Word、Excel 和 PowerPoint)与 1 TB 的 OneDrive 云存储空间、高级安全性等结合在一起,可帮助你在任何设备上完成操作。

    5 引用 • 34 回帖 • 1 关注
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    151 引用 • 988 回帖
  • abitmean

    有点意思就行了

    37 关注
  • H2

    H2 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,不受平台的限制,同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。

    11 引用 • 54 回帖 • 667 关注
  • 导航

    各种网址链接、内容导航。

    43 引用 • 177 回帖
  • NGINX

    NGINX 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。 NGINX 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本 0.1.0 发布于 2004 年 10 月 4 日。

    315 引用 • 547 回帖
  • 开源中国

    开源中国是目前中国最大的开源技术社区。传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现、使用、并交流开源技术的平台。目前开源中国社区已收录超过两万款开源软件。

    7 引用 • 86 回帖
  • 浅吟主题

    Jeffrey Chen 制作的思源笔记主题,项目仓库:https://github.com/TCOTC/Whisper

    1 引用 • 28 回帖
  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    286 引用 • 248 回帖
  • Solidity

    Solidity 是一种智能合约高级语言,运行在 [以太坊] 虚拟机(EVM)之上。它的语法接近于 JavaScript,是一种面向对象的语言。

    3 引用 • 18 回帖 • 438 关注
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    5 引用 • 16 回帖 • 3 关注
  • HBase

    HBase 是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的 Google 论文 “Bigtable:一个结构化数据的分布式存储系统”。就像 Bigtable 利用了 Google 文件系统所提供的分布式数据存储一样,HBase 在 Hadoop 之上提供了类似于 Bigtable 的能力。

    17 引用 • 6 回帖 • 60 关注
  • 旅游

    希望你我能在旅途中找到人生的下一站。

    95 引用 • 901 回帖
  • Python

    Python 是一种面向对象、直译式电脑编程语言,具有近二十年的发展历史,成熟且稳定。它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务。它的语法简捷和清晰,尽量使用无异义的英语单词,与其它大多数程序设计语言使用大括号不一样,它使用缩进来定义语句块。

    556 引用 • 675 回帖 • 1 关注