c#将平铺列表转换为树形结构(支持孤儿节点作为独立根节点)

cs 复制代码
 public class ListToTree<T> where T : new()
 {
     /// <summary>
     /// 将平铺列表转换为树形结构(标准版本)
     /// </summary>
     /// <param name="lists">原始平铺数据列表</param>
     /// <param name="code">节点唯一标识字段名(如 "id")</param>
     /// <param name="parentCode">父节点标识字段名(如 "parent")</param>
     /// <param name="value">根节点的父节点值(通常为 "0" 或空字符串)</param>
     /// <param name="childNodeName">子节点集合的字段名,默认为 "children"</param>
     /// <returns>树形结构的列表</returns>
     public static List<T> ListToTrees(List<T> lists, string code, string parentCode, string value, string childNodeName = "children")
     {
         if (lists.Count > 0)
         {
             var tempLists = lists
                 .Where(m => (string?)m?.GetType().GetProperty(parentCode)?.GetValue(m, null) == value).ToList();
             if (tempLists.Count > 0)
             {
                 var treeDatas = new List<T>();
                 T t;
                 Type? type;
                 foreach (var obj in tempLists)
                 {
                     t = obj;
                     type = obj?.GetType();

                     var childs = ListToTrees(lists, code, parentCode,
                         (string)(type?.GetProperty(code)?.GetValue(t, null) ?? ""), childNodeName);
                     if (childs.Count > 0)
                     {
                         type?.GetProperty(childNodeName)?.SetValue(t, childs, null);
                     }

                     treeDatas.Add(t);
                 }

                 return treeDatas;
             }
         }

         return new List<T>();
     }

     /// <summary>
     /// 将平铺列表转换为树形结构(支持孤儿节点作为独立根节点)
     /// 当某个节点的父节点在列表中不存在时,该节点自身作为根节点保留在结果中
     /// </summary>
     /// <param name="lists">原始平铺数据列表</param>
     /// <param name="code">节点唯一标识字段名(如 "id")</param>
     /// <param name="parentCode">父节点标识字段名(如 "parent")</param>
     /// <param name="value">根节点的父节点值(通常为 "0" 或空字符串)</param>
     /// <param name="childNodeName">子节点集合的字段名,默认为 "children"</param>
     /// <returns>树形结构的列表,包含所有节点(包括孤儿节点)</returns>
     public static List<T> ListToTreesWithOrphan(List<T> lists, string code, string parentCode, string value, string childNodeName = "children")
     {
         var result = new List<T>();
         var processedIds = new HashSet<string>();

         var rootNodes = lists
             .Where(m => (string?)m?.GetType().GetProperty(parentCode)?.GetValue(m, null) == value)
             .ToList();

         foreach (var root in rootNodes)
         {
             BuildTree(lists, root, code, parentCode, childNodeName, processedIds, result);
         }

         var orphanNodes = lists
             .Where(m => !processedIds.Contains((string)(m?.GetType().GetProperty(code)?.GetValue(m, null) ?? "")))
             .ToList();

         foreach (var orphan in orphanNodes)
         {
             BuildTree(lists, orphan, code, parentCode, childNodeName, processedIds, result);
         }

         return result;
     }

     /// <summary>
     /// 递归构建子树(内部方法)
     /// </summary>
     /// <param name="lists">原始平铺数据列表</param>
     /// <param name="node">当前节点</param>
     /// <param name="code">节点唯一标识字段名</param>
     /// <param name="parentCode">父节点标识字段名</param>
     /// <param name="childNodeName">子节点集合的字段名</param>
     /// <param name="processedIds">已处理过的节点ID集合(用于去重)</param>
     /// <param name="result">最终结果列表</param>
     private static void BuildTree(List<T> lists, T node, string code, string parentCode, string childNodeName, HashSet<string> processedIds, List<T> result)
     {
         var type = node?.GetType();
         var nodeId = (string)(type?.GetProperty(code)?.GetValue(node, null) ?? "");

         if (processedIds.Contains(nodeId))
             return;

         processedIds.Add(nodeId);

         var children = lists
             .Where(m => (string?)m?.GetType().GetProperty(parentCode)?.GetValue(m, null) == nodeId)
             .ToList();

         if (children.Count > 0)
         {
             var childList = new List<T>();
             foreach (var child in children)
             {
                 BuildTree(lists, child, code, parentCode, childNodeName, processedIds, childList);
             }
             type?.GetProperty(childNodeName)?.SetValue(node, childList, null);
         }

         result.Add(node);
     }
 }

使用方法 :

cs 复制代码
 public async Task<List<MenuListVO>> GetMenuList(MenuListDTO menuListDTO)
 {
     List<IConditionalModel> conditionalModels = new List<IConditionalModel>();
     var query = db.Queryable<h_bd_menu>().AS("h_bd_menu")
         .WhereIF(!StringExtension.IsNullOrEmpty(menuListDTO.title), v => v.title.Contains(menuListDTO.title));
     if (!StringExtension.IsNullOrEmpty(menuListDTO.query))
     {
         conditionalModels = db.Utilities.JsonToConditionalModels(menuListDTO.query.ToJson());
     }

     var list = await query.Where(conditionalModels).ToListAsync();
     ConsoleHelper.WriteLine(list.ToJson());
     var rootvalue = list.Any() ? list.Min(v => v.parent) : 0;
     var menuTree = ListToTree<MenuListVO>.ListToTreesWithOrphan(App.Mapper.MapTo<List<MenuListVO>>(list), "id", "parent", rootvalue.ToString());
     ConsoleHelper.WriteLine(menuTree.ToJson());

     return App.Mapper.MapTo<List<MenuListVO>>(menuTree);
 }

转换前数据

javascript 复制代码
[
    {
        "title": "测试表单",
        "number": "test",
        "component": "layouts/DetailLayout.vue",
        "desc": "",
        "name": "AppTest",
        "icon": "canyinkafei",
        "sortid": 1,
        "parent": 0,
        "islock": false,
        "id": 2039617586328956928,
        "created_by": 1913844754068672512,
        "created_time": "2026-04-02 16:15:01",
        "IsDeleted": false
    },
    {
        "title": "表单",
        "number": "test-pending",
        "component": "",
        "desc": "二次封装表单",
        "name": "AppTestTestPending",
        "icon": "jiaoseguanli1",
        "sortid": 1,
        "parent": 2039617586328956928,
        "islock": false,
        "id": 2039629889204056064,
        "created_by": 1913844754068672512,
        "created_time": "2026-04-02 17:03:54",
        "IsDeleted": false
    },
    {
        "title": "表单原版",
        "number": "form",
        "component": "views/template/Form.vue",
        "desc": "",
        "name": "AppTestTestPendingForm",
        "icon": "geren-wodeweituo",
        "sortid": 1,
        "parent": 2039629889204056064,
        "islock": true,
        "id": 2039642027431751680,
        "created_by": 1913844754068672512,
        "created_time": "2026-04-02 17:52:08",
        "IsDeleted": false
    },
    {
        "title": "配置表单",
        "number": "config-form",
        "component": "views/template/ConfigFormExample.vue",
        "desc": "",
        "name": "AppTestTestPendingConfigForm",
        "icon": "canyinkafei",
        "sortid": 3,
        "parent": 2039629889204056064,
        "islock": false,
        "id": 2039642271640907776,
        "created_by": 1913844754068672512,
        "created_time": "2026-04-02 17:53:06",
        "IsDeleted": false
    },
    {
        "title": "菜单管理",
        "number": "menumanage",
        "component": "views/menu/index.vue",
        "desc": "",
        "name": "AppTestTestPendingMenuManage",
        "icon": "geren-wodeweituo",
        "sortid": 1,
        "parent": 2039642863855661056,
        "islock": false,
        "id": 2039642995829436416,
        "created_by": 1913844754068672512,
        "created_time": "2026-04-02 17:55:59",
        "IsDeleted": false
    }
]

转化后数据

javascript 复制代码
[
    {
        "id": "2039617586328956928",
        "title": "测试表单",
        "number": "test",
        "component": "layouts/DetailLayout.vue",
        "desc": "",
        "name": "AppTest",
        "icon": "canyinkafei",
        "sortid": "1",
        "parent": "0",
        "islock": false,
        "children": [
            {
                "id": "2039629889204056064",
                "title": "表单",
                "number": "test-pending",
                "component": "",
                "desc": "二次封装表单",
                "name": "AppTestTestPending",
                "icon": "jiaoseguanli1",
                "sortid": "1",
                "parent": "2039617586328956928",
                "islock": false,
                "children": [
                    {
                        "id": "2039642027431751680",
                        "title": "表单原版",
                        "number": "form",
                        "component": "views/template/Form.vue",
                        "desc": "",
                        "name": "AppTestTestPendingForm",
                        "icon": "geren-wodeweituo",
                        "sortid": "1",
                        "parent": "2039629889204056064",
                        "islock": true
                    },
                    {
                        "id": "2039642271640907776",
                        "title": "配置表单",
                        "number": "config-form",
                        "component": "views/template/ConfigFormExample.vue",
                        "desc": "",
                        "name": "AppTestTestPendingConfigForm",
                        "icon": "canyinkafei",
                        "sortid": "3",
                        "parent": "2039629889204056064",
                        "islock": false
                    }
                ]
            }
        ]
    },
    {
        "id": "2039642995829436416",
        "title": "菜单管理",
        "number": "menumanage",
        "component": "views/menu/index.vue",
        "desc": "",
        "name": "AppTestTestPendingMenuManage",
        "icon": "geren-wodeweituo",
        "sortid": "1",
        "parent": "2039642863855661056",
        "islock": false
    }
]
相关推荐
每天都要加油呀!10 小时前
多租户中间件适配
java·多租户
当时只道寻常10 小时前
Vue3 + IntersectionObserver 实现高性能图片懒加载
前端
sakiko_10 小时前
UIKit学习笔记3-布局、滚动视图、隐藏或显示视图
前端·笔记·学习·objective-c·swift·uikit
014-code10 小时前
Java 并发中的原子类
java·开发语言·并发
AI人工智能+电脑小能手10 小时前
【大白话说Java面试题】【Java基础篇】第29题:静态代理和动态代理的区别是什么
java·开发语言·后端·面试·代理模式
善恶怪客10 小时前
Java-数组和可变参数
java·开发语言
小编码上说10 小时前
LSH(局部敏感哈希)分桶,海量数据下的相似性搜索解决方案
java·spring boot·缓存·langchain4j·lsh·局部敏感哈希·ai调用优化
计算机_毕业设计11 小时前
java-springboot数字藏品系统 基于 SpringBoot 的区块链数字艺术品交易平台 Java 微服务架构下的加密藏品展示与拍卖系统计算机毕业设计
java·spring boot·课程设计
ONVO ncen11 小时前
Redis6.2.6下载和安装
java
丑八怪大丑11 小时前
JDK8-17新特性
java·开发语言