如何在JavaScript中实现运行时动态将二维数组转嵌套结构体?
实现动态结构体驱动的二维数组转嵌套数据结构
需求是将首行为表头的二维数组,根据运行时动态确定的结构体定义,转换为对应的嵌套数据结构。以Northwind数据库的客户-订单-订单明细数据为例,此前尝试递归+访问栈赋值、转置数据记录唯一值起始索引等方法均未完成深层嵌套数组的正确赋值,现提供transformData函数的实现方案。
示例输入数据
// 首行为表头的二维数组 const inputData = [ ["CustomerID", "CompanyName", "OrderID", "OrderDate", "ProductID", "ProductName", "Quantity"], ["ALFKI", "Alfreds Futterkiste", "10643", "1997-08-25", "28", "Rössle Sauerkraut", "15"], ["ALFKI", "Alfreds Futterkiste", "10643", "1997-08-25", "39", "Chartreuse verte", "21"], ["ALFKI", "Alfreds Futterkiste", "10692", "1997-10-03", "46", "Spegesild", "20"], ["ANATR", "Ana Trujillo Emparedados y helados", "10308", "1996-09-18", "11", "Queso Cabrales", "10"], ["ANATR", "Ana Trujillo Emparedados y helados", "10308", "1996-09-18", "42", "Singaporean Hokkien Fried Mee", "10"], ];
目标结构体定义
// 运行时确定的嵌套结构描述: // - 普通字段用空字符串标识 // - 数组子结构用 {type: "array", schema: 子结构} 标识 const targetSchema = { CustomerID: "", CompanyName: "", orders: { type: "array", schema: { OrderID: "", OrderDate: "", orderDetails: { type: "array", schema: { ProductID: "", ProductName: "", Quantity: "" } } } } };
期望转换结果
const expectedOutput = [ { "CustomerID": "ALFKI", "CompanyName": "Alfreds Futterkiste", "orders": [ { "OrderID": "10643", "OrderDate": "1997-08-25", "orderDetails": [ { "ProductID": "28", "ProductName": "Rössle Sauerkraut", "Quantity": "15" }, { "ProductID": "39", "ProductName": "Chartreuse verte", "Quantity": "21" } ] }, { "OrderID": "10692", "OrderDate": "1997-10-03", "orderDetails": [ { "ProductID": "46", "ProductName": "Spegesild", "Quantity": "20" } ] } ] }, { "CustomerID": "ANATR", "CompanyName": "Ana Trujillo Emparedados y helados", "orders": [ { "OrderID": "10308", "OrderDate": "1996-09-18", "orderDetails": [ { "ProductID": "11", "ProductName": "Queso Cabrales", "Quantity": "10" }, { "ProductID": "42", "ProductName": "Singaporean Hokkien Fried Mee", "Quantity": "10" } ] } ] } ];
解决方案:transformData函数实现
function transformData(inputArray, schema) { // 提取表头和数据行,生成表头-索引映射表 const [headers, ...rows] = inputArray; const headerIndexMap = headers.reduce((map, header, idx) => { map[header] = idx; return map; }, {}); // 递归处理层级分组与结构构建 function buildGroupedData(data, currentSchema) { // 拆分当前层级的普通字段和数组子结构 const nonArrayFields = Object.entries(currentSchema).filter(([_, config]) => config.type !== "array"); const arrayFields = Object.entries(currentSchema).filter(([_, config]) => config.type === "array"); // 生成当前层级的唯一分组键:用所有普通字段的值拼接(保证同组数据属于同一父节点) const getGroupKey = (row) => { return nonArrayFields.map(([field]) => row[headerIndexMap[field]]).join("|"); }; // 按分组键对数据行分组 const groups = rows.reduce((acc, row) => { const key = getGroupKey(row); if (!acc[key]) acc[key] = []; acc[key].push(row); return acc; }, {}); // 遍历分组,构建当前层级对象并递归处理子数组 return Object.values(groups).map(groupRows => { const item = {}; // 填充普通字段(同组内值一致,取第一行即可) nonArrayFields.forEach(([field]) => { item[field] = groupRows[0][headerIndexMap[field]]; }); // 递归处理数组子结构 arrayFields.forEach(([fieldName, { schema: childSchema }]) => { item[fieldName] = buildGroupedData([headers, ...groupRows], childSchema); }); return item; }); } return buildGroupedData(inputArray, schema); }
核心逻辑说明
- 表头映射:快速定位字段在数据行中的索引,避免重复遍历表头。
- 层级分组:每个层级用当前结构的普通字段生成唯一键,将数据行按父节点分组(比如同一客户的所有订单行、同一订单的所有明细行)。
- 递归构建:先填充当前层级的普通属性,再将分组数据传入下一层级,递归完成嵌套数组的构建。
测试验证
const result = transformData(inputData, targetSchema); console.log(JSON.stringify(result) === JSON.stringify(expectedOutput)); // 输出 true
内容的提问来源于stack exchange,提问作者benedikt hamberger
相关产品推荐
相关产品推荐

