递归转换嵌套JSON为扁平结构并添加Id、typography、active字段
嵌套JSON转扁平JSON并添加标识字段(适配ReactDND)
问题描述
需要为嵌套JSON中的每个对象添加唯一ID、typography层级标识和active布尔字段,再转换为扁平JSON格式,用于ReactDND库。
原始嵌套JSON
[{ "menuHead": [ { "name": "MenuHead 01", "description": "sample Head 01", "icon": "sample01.png", "products": [] }, { "name": "MenuHead 02", "description": "sample Head 01", "icon": "sample01.png", "products01": [ { "name": "product 01", "description": "sample product ONE", "icon": "product01.png", "section": [ { "name": "section01", "description": "ActiveLayer", "icon": "img.png", "min": 100, "max": 1000 }, { "name": "section02", "description": "ActiveLayer", "icon": "img.png", "min": 500, "max": 1000 }, { "name": "section03", "description": "ActiveLayer", "icon": "img.png", "min": 100, "max": 600 } ] }, { "name": "product 02", "description": "sample product ONE", "icon": "product01.png", "section": [ { "name": "section001", "description": "ActiveLayer", "icon": "img.png", "min": 100, "max": 1000 }, { "name": "section002", "description": "ActiveLayer", "icon": "img.png", "min": 500, "max": 1000 }, { "name": "section003", "description": "ActiveLayer", "icon": "img.png", "min": 100, "max": 600 } ] } ] } ] }]
需求规则
- 为每个对象添加唯一Id、typography层级字段和active布尔字段;
- section层级对象的typography统一为2,active为false;
- menuHead层级对象的typography为0,active为true;
- products层级对象的typography为1,active为true;
- 需采用递归方式实现,避免硬编码键名(如menuHead、products、section等),因为后续键名可能变更。
期望输出的扁平JSON
[ { "name":"MenuHead 01", "description":"sample Head 01", "icon":"sample01.png", "id":1, "typography":0, "active":true }, { "name":"MenuHead 02", "description":"sample Head 01", "icon":"sample01.png", "id":2, "typography":0, "active":true }, { "name":"product 01", "description":"sample product ONE", "icon":"product01.png", "id":3, "typography":1, "active":true }, { "name":"section01", "description":"ActiveLayer", "icon":"img.png", "min":100, "max":1000, "id":4, "typography":2, "active":false }, { "name":"section02", "description":"ActiveLayer", "icon":"img.png", "min":500, "max":1000, "id":5, "typography":2, "active":false }, { "name":"section03", "description":"ActiveLayer", "icon":"img.png", "min":100, "max":600, "id":6, "typography":2, "active":false }, { "name":"product 02", "description":"sample product ONE", "icon":"product01.png", "id":7, "typography":1, "active":true }, { "name":"section001", "description":"ActiveLayer", "icon":"img.png", "min":100, "max":1000, "id":8, "typography":2, "active":false }, { "name":"section002", "description":"ActiveLayer", "icon":"img.png", "min":500, "max":1000, "id":9, "typography":2, "active":false }, { "name":"section003", "description":"ActiveLayer", "icon":"img.png", "min":100, "max":600, "id":10, "typography":2, "active":false } ]
错误代码(运行出错)
const tree = [ { "menuHead":[ { "name":"MenuHead 01", "description":"sample Head 01", "icon":"sample01.png", "products":[ { "name":"product 01", "description":"sample product ONE", "icon":"product01.png", "sections":[ { "name":"section01", "description":"ActiveLayer", "icon":"img.png", "min":100, "max":1000 }, { "name":"section02", "description":"ActiveLayer", "icon":"img.png", "min":500, "max":1000 }, { "name":"section03", "description":"ActiveLayer", "icon":"img.png", "min":100, "max":600 } ] } ] } ] } ] function addUniqueID(arr: any[], i: number) { arr.forEach((obj) => { obj.typography = i; obj.id = i + 1; obj.active = true; if (obj.menuHead) { addUniqueID(obj.menuHead, i + 1); } else if (obj.sections) { addUniqueID(obj.sections, i + 1); } else if (obj.products) { addUniqueID(obj.products, i + 1); } else { obj.active = false; } }); } addUniqueID(tree, 0); console.log(tree) const c = (item: any) => "name" in item ? [{ name: item.name, id: item.id, typography: item.typography, active: item.active, description:item.description, icon: item.icon, min:item.min, max:item.max }] : [] function flat(item: any) { if ("products" in item) { return c(item).concat(item.products.flatMap(flat)) } if ("sections" in item) { return c(item).concat(item.sections.flatMap(flat)) } return ( c(item) ) } console.log(JSON.stringify (tree.flatMap(flat)))
修正后的实现代码
// 原始嵌套JSON数据 const rawData = [ { "menuHead": [ { "name": "MenuHead 01", "description": "sample Head 01", "icon": "sample01.png", "products": [] }, { "name": "MenuHead 02", "description": "sample Head 01", "icon": "sample01.png", "products01": [ { "name": "product 01", "description": "sample product ONE", "icon": "product01.png", "section": [ { "name": "section01", "description": "ActiveLayer", "icon": "img.png", "min": 100, "max": 1000 }, { "name": "section02", "description": "ActiveLayer", "icon": "img.png", "min": 500, "max": 1000 }, { "name": "section03", "description": "ActiveLayer", "icon": "img.png", "min": 100, "max": 600 } ] }, { "name": "product 02", "description": "sample product ONE", "icon": "product01.png", "section": [ { "name": "section001", "description": "ActiveLayer", "icon": "img.png", "min": 100, "max": 1000 }, { "name": "section002", "description": "ActiveLayer", "icon": "img.png", "min": 500, "max": 1000 }, { "name": "section003", "description": "ActiveLayer", "icon": "img.png", "min": 100, "max": 600 } ] } ] } ] } ]; // 生成唯一ID的计数器 let idCounter = 1; /** * 递归处理嵌套对象,添加标识字段并收集扁平数据 * @param data 待处理数据(对象或数组) * @param currentLevel 当前层级(用于确定typography) * @returns 扁平后的数组 */ function processAndFlatten(data: any, currentLevel: number): any[] { const result: any[] = []; // 处理数组:遍历每个元素递归处理 if (Array.isArray(data)) { data.forEach(item => { result.push(...processAndFlatten(item, currentLevel)); }); return result; } // 处理非数组对象:先判断是否是业务对象(有name字段) if (data.name) { // 根据层级设置typography和active let typography = currentLevel; let active = true; // 所有深度≥2的层级(section)强制typography=2,active=false if (currentLevel >= 2) { typography = 2; active = false; } // 复制原对象的所有字段,添加标识字段 const processedItem = { ...data, id: idCounter++, typography, active }; result.push(processedItem); } // 递归处理当前对象的所有数组类型属性(不硬编码键名) Object.keys(data).forEach(key => { const value = data[key]; if (Array.isArray(value)) { // 数组属性的层级+1 result.push(...processAndFlatten(value, currentLevel + 1)); } }); return result; } // 执行处理:从层级0开始,传入menuHead数组 const flattenedData = processAndFlatten(rawData[0].menuHead, 0); console.log(JSON.stringify(flattenedData, null, 2));
关键修改说明
- 移除硬编码键名:通过遍历对象所有键,自动识别数组类型属性进行递归处理,适配任意键名变更
- 唯一ID生成:用全局计数器保证每个对象ID唯一,解决原代码中ID重复问题
- 层级规则适配:
- 根层级(menuHead)typography=0,active=true
- 中间层级(products类)typography=1,active=true
- 所有深度≥2的层级(section类)强制typography=2,active=false
- 合并逻辑:将添加标识字段与扁平化合并为一个递归函数,减少遍历冗余
- 兼容性:支持任意数组属性名(如
products01),无需修改代码即可适配
内容的提问来源于stack exchange,提问作者tinyVariant
相关产品推荐
相关产品推荐

