如何在嵌套对象数组中匹配flatData并更新matchFound属性
问题描述
我有一个嵌套对象数组treeData,需要和扁平数组flatData对比值,更新其中的matchFound属性,规则如下:
- 若父节点的
matchFound设为true,其所有子节点的matchFound也必须设为true。
示例
- 当
flatData存在{ make: "BMW" }时,需将treeData中对应make为BMW的节点matchFound设为true,同时将其所有子节点的matchFound设为true。 - 当
flatData存在{ make: "Infiniti", model: "G50", trim: "Luxe" }时,仅需将treeData中对应trim为Luxe的节点matchFound设为true。
我现在的核心问题是没法通过flatData在treeData里准确定位对应节点,如果调整flatData的数据结构能解决问题,我可以调整。我试过把flatData转换成treeData结构,但没用到点子上;也写了一段代码,但还是定位不了节点。
现有数据结构
treeData
treeData = [{ field: 'make', name: 'Infiniti', matchFound: null, children: [{ field: 'model', name: 'G50', matchFound: null, children: [{ field: 'trim', name: 'Pure AWD', matchFound: null, }, { field: 'trim', name: 'Luxe', matchFound: null, }, ], }, { field: 'model', name: 'QX50', matchFound: null, children: [{ field: 'trim', name: 'Pure AWD', matchFound: null }, { field: 'trim', name: 'Luxe', matchFound: null, }, ], }, ], }, { field: 'make', name: 'BMW', matchFound: null, children: [{ field: 'model', name: '2 Series', matchFound: null, children: [{ field: 'trim', name: 'Coupé', matchFound: null, }, { field: 'trim', name: 'Gran Coupé', matchFound: null, }, ], }, { field: 'model', name: '3 Series', matchFound: null, children: [{ field: 'trim', name: 'Sedan', matchFound: null, }, { field: 'trim', name: 'PHEV', matchFound: null, }, ], }, ], }, ];
flatData
flatData = [{ make: "Infiniti", model: "G50", trim: "Luxe" }, { make: "Infiniti", model: "G50", trim: "Pure AWD" }, { make: "BMW" }]
期望转换后的效果(参考)
[{ field: 'make', name: 'Infiniti', matchFound: null, children: [{ field: 'model', name: 'G50', matchFound: null, children: [{ field: 'trim', name: 'Pure AWD', matchFound: true, }, { field: 'trim', name: 'Luxe', matchFound: true, }, ], }, ], }, { field: 'make', name: 'BMW', matchFound: true }, ];
我尝试的代码
for (const item of this.flatData) { for (const [key, value] of Object.entries(item)) { for (let i = 0; i < this.treeData.length; i++) { if (this.treeData[i].name === `${value}`) { this.treeData[i].matchFound = true; if (this.treeData[i].hasOwnProperty('children')) { this.updateAllChildren(i, this.treeData[i].children); } } } } } // Set matchFound true for all the children function updateAllChildren(index, items) { for (let j = 0; j < items.length; j++) { this.treeData[index].children[j].matchFound = true; if (this.treeData[index].children[j].hasOwnProperty('children')) { for (let k = 0; k < this.treeData[index].children[j].children.length; k++) { this.treeData[index].children[j].children[k].matchFound = true; } } } }
解决方案
你的核心问题是没按层级匹配节点——原来的代码只匹配第一层节点,没根据flatData的完整层级路径定位深层节点,而且updateAllChildren写法死板,只能处理固定两层子节点,扩展性差。
步骤1:把flatData转换成「层级路径数组」
先把每个flatItem转换成按make→model→trim顺序的路径数组,方便后续按层级遍历treeData:
// 定义固定层级顺序,和treeData的field层级对应 const levelOrder = ['make', 'model', 'trim']; // 把flatData转换成路径数组 const paths = flatData.map(item => { return levelOrder.map(key => item[key]).filter(Boolean); }); // 转换后结果: // [["Infiniti", "G50", "Luxe"], ["Infiniti", "G50", "Pure AWD"], ["BMW"]]
步骤2:编写递归遍历树的工具函数
写两个递归函数,处理节点标记和层级查找:
// 标记目标节点及其所有子节点为true function markNodeAndChildren(node) { node.matchFound = true; if (node.children?.length) { node.children.forEach(child => markNodeAndChildren(child)); } } // 按路径递归查找节点,找到后调用标记函数 function findAndMarkNode(nodes, path) { if (!path.length) return; const currentValue = path[0]; const targetNode = nodes.find(node => node.name === currentValue); if (!targetNode) return; // 路径最后一层:标记当前节点及所有子节点 // 非最后一层:继续递归查找子节点 if (path.length === 1) { markNodeAndChildren(targetNode); } else { findAndMarkNode(targetNode.children || [], path.slice(1)); } }
步骤3:执行匹配和标记
先初始化所有节点的matchFound为null(可选,确保初始状态统一),再遍历路径处理treeData:
// 初始化treeData的matchFound状态 function initTree(nodes) { nodes.forEach(node => { node.matchFound = null; if (node.children?.length) initTree(node.children); }); } initTree(treeData); // 处理每个路径 paths.forEach(path => findAndMarkNode(treeData, path));
方案优势
- 按固定层级匹配,精准对应treeData的嵌套结构,不会出现匹配错误
- 递归函数支持任意深度的子节点,后续扩展层级(比如加year字段)只需修改
levelOrder数组 - 逻辑清晰,维护成本低
运行后treeData会完全符合期望效果:BMW节点及其所有子节点matchFound为true,Infiniti→G50下的两个trim节点matchFound为true,其余节点保持null。
内容的提问来源于stack exchange,提问作者Jason22
相关产品推荐
相关产品推荐

