C#如何基于非规范化记录最优实现树形菜单,避免LINQ递归写法
需求说明
需要基于非规范化的体育赛事记录生成三级树形菜单,原始记录为SPORT(运动项目)、LEAGUE(联赛)、MATCH(赛事)的扁平结构,样例数据如下:
| SPORT | LEAGUE | MATCH |
|---|---|---|
| Sport1 | League1 | Match1 |
| Sport1 | League1 | Match2 |
| Sport1 | League2 | Match1 |
| Sport2 | League1 | Match1 |
现有实现痛点
当前采用多层嵌套LINQ GroupBy的写法,层级固定扩展性差,调整菜单层级需要修改嵌套结构,现有代码如下(原有代码存在MenuItem构造函数未给SubMenuItems赋值的bug,生成的节点子菜单会为空):
// 现有嵌套GroupBy实现 sportMenu.GroupBy(x => x.Sport) .Select(x => MenuItem.Create(x.Key, x.GroupBy(x => x.League) .Select(x => MenuItem.Create(x.Key, x.GroupBy(x => x.Match) .Select( x =>MenuItem.Create(x.Key, new List<MenuItem>())))) .ToList())); // MenuItem定义 public class MenuItem { public string Name { get; set; } public List<MenuItem> SubMenuItems { get; set; } public static MenuItem Create(string name, List<MenuItem> subMenuItems) { return new MenuItem(name.Trim(), subMenuItems); } private MenuItem(string name, List<MenuItem> subMenuItems) { Name = name; SubMenuItems = subMenuItems; } }
优化实现(无嵌套GroupBy)
采用逐行遍历+字典缓存节点的写法,层级逻辑清晰,后续新增/减少菜单层级只需调整遍历逻辑即可,无需嵌套代码:
// 原始记录实体类定义 public class SportRecord { public string Sport { get; set; } public string League { get; set; } public string Match { get; set; } } // 菜单生成方法 public List<MenuItem> BuildSportMenu(List<SportRecord> records) { // 缓存已创建的运动项目节点 var sportDict = new Dictionary<string, MenuItem>(); // 缓存已创建的联赛节点,key格式为 运动项目名_联赛名 避免重名冲突 var leagueDict = new Dictionary<string, MenuItem>(); var rootMenu = new List<MenuItem>(); foreach (var record in records) { // 处理第一级:运动项目节点 if (!sportDict.TryGetValue(record.Sport, out var sportNode)) { sportNode = MenuItem.Create(record.Sport, new List<MenuItem>()); sportDict.Add(record.Sport, sportNode); rootMenu.Add(sportNode); } // 处理第二级:联赛节点 var leagueKey = $"{record.Sport}_{record.League}"; if (!leagueDict.TryGetValue(leagueKey, out var leagueNode)) { leagueNode = MenuItem.Create(record.League, new List<MenuItem>()); leagueDict.Add(leagueKey, leagueNode); sportNode.SubMenuItems.Add(leagueNode); } // 处理第三级:赛事节点 var matchNode = MenuItem.Create(record.Match, new List<MenuItem>()); leagueNode.SubMenuItems.Add(matchNode); } return rootMenu; }
- 该实现时间复杂度为O(n),仅需遍历一次原始记录,性能优于嵌套GroupBy的O(n*层级数)
- 如需新增第四级节点(比如赛事阶段),只需新增对应层级的字典缓存,在循环内补充对应逻辑即可
内容的提问来源于stack exchange,提问作者PCH
相关产品推荐
相关产品推荐

