在C#中将扁平数据集转换为嵌套层级结构的高效实现方法
实现扁平数据到嵌套模型的高效转换
这个需求太常见了——把从关联多外键的数据库视图中查询到的扁平数据,聚合分组转换成嵌套的对象模型。最简便高效的方式是通过一次遍历完成分组聚合,时间复杂度为O(n),只需要遍历一次原数据即可,完全不需要额外的嵌套循环。
先明确输入和期望输出
输入扁平数据(整理为结构化对象数组)
const flatData = [ { Id: 1, Name: 'N1', TagId: 'T1', DepartmentId: null, Code: null }, { Id: 1, Name: 'N1', TagId: 'T2', DepartmentId: null, Code: null }, { Id: 1, Name: 'N1', TagId: null, DepartmentId: 'D1', Code: null }, { Id: 1, Name: 'N1', TagId: null, DepartmentId: null, Code: 'C1' }, { Id: 1, Name: 'N1', TagId: null, DepartmentId: null, Code: 'C2' }, { Id: 2, Name: 'N2', TagId: 'T3', DepartmentId: null, Code: null }, { Id: 2, Name: 'N2', TagId: null, DepartmentId: 'D2', Code: null } ];
期望输出的嵌套模型
[ { Id: 1, Name: 'N1', TagIds: ['T1', 'T2'], DepartmentId: 'D1', Codes: ['C1', 'C2'] }, { Id: 2, Name: 'N2', TagIds: ['T3'], DepartmentId: 'D2', Codes: [] } ]
具体实现方案
前端JavaScript版本(用reduce方法)
reduce是数组原生方法,专门用来做累加/聚合操作,非常适合这个场景:
const aggregatedData = Object.values(flatData.reduce((acc, item) => { // 用Id作为唯一分组键 const groupKey = item.Id; // 如果当前分组不存在,初始化嵌套结构 if (!acc[groupKey]) { acc[groupKey] = { Id: item.Id, Name: item.Name, TagIds: [], DepartmentId: null, Codes: [] }; } // 非空TagId加入数组 if (item.TagId) acc[groupKey].TagIds.push(item.TagId); // 非空DepartmentId直接赋值(假设每个Id对应唯一部门) if (item.DepartmentId) acc[groupKey].DepartmentId = item.DepartmentId; // 非空Code加入数组 if (item.Code) acc[groupKey].Codes.push(item.Code); return acc; }, {}));
后端Python版本(用字典分组)
如果是在后端处理,用字典作为分组容器的思路完全一致:
flat_data = [ {"Id": 1, "Name": "N1", "TagId": "T1", "DepartmentId": None, "Code": None}, {"Id": 1, "Name": "N1", "TagId": "T2", "DepartmentId": None, "Code": None}, {"Id": 1, "Name": "N1", "TagId": None, "DepartmentId": "D1", "Code": None}, {"Id": 1, "Name": "N1", "TagId": None, "DepartmentId": None, "Code": "C1"}, {"Id": 1, "Name": "N1", "TagId": None, "DepartmentId": None, "Code": "C2"}, {"Id": 2, "Name": "N2", "TagId": "T3", "DepartmentId": None, "Code": None}, {"Id": 2, "Name": "N2", "TagId": None, "DepartmentId": "D2", "Code": None} ] aggregated_map = {} for item in flat_data: key = item["Id"] if key not in aggregated_map: aggregated_map[key] = { "Id": item["Id"], "Name": item["Name"], "TagIds": [], "DepartmentId": None, "Codes": [] } # 处理各字段 if item["TagId"] is not None: aggregated_map[key]["TagIds"].append(item["TagId"]) if item["DepartmentId"] is not None: aggregated_map[key]["DepartmentId"] = item["DepartmentId"] if item["Code"] is not None: aggregated_map[key]["Codes"].append(item["Code"]) # 把字典值转成列表就是最终结果 aggregated_data = list(aggregated_map.values())
额外思路:数据库层面聚合
如果你的扁平数据是直接从数据库视图查询出来的,也可以考虑在数据库层面完成聚合:
- 对于MySQL,可以用
GROUP_CONCAT函数把TagId和Code拼接成逗号分隔的字符串,查询后在应用层转成数组; - 对于PostgreSQL/SQL Server,可以用
STRING_AGG函数做类似操作。
不过如果已经拿到了扁平数据,应用层的一次遍历聚合是最直接高效的选择。
内容的提问来源于stack exchange,提问作者Shyamal Parikh
相关产品推荐
相关产品推荐

