如何按isOptional字段拆分递归数组并保留父路径结构
递归拆分嵌套子项结构:分离isOptional不同值的完整路径
需求说明
处理一个含递归层级的packagedItems结构(子项同样包含item和嵌套子项,层级无限制),将所有包含isOptional: true的节点路径、以及isOptional: false的节点路径分别拆分,返回两个与输入结构完全一致的数组。
输入示例
const product = { name: "Product 1", packagedItems: [ { id: 1, isOptional: false, item: { name: "1#1", packagedItems: [ { id: 3, isOptional: false, item: { name: "2#1", packagedItems: [ { id: 5, isOptional: false, item: { name: "3#1", packagedItems: [] } }, { id: 6, isOptional: true, item: { name: "3#2", packagedItems: [] } } ] } } ] } }, { id: 2, isOptional: false, item: { name: "1#2", packagedItems: [ { id: 4, isOptional: false, item: { name: "2#2", packagedItems: [ { id: 7, isOptional: true, item: { name: "3#3", packagedItems: [] } }, { id: 8, isOptional: true, item: { name: "3#4", packagedItems: [] } } ] } } ] } } ] };
尝试的代码(仅获取父级名称,无法构建完整结构)
const getParents = ( packagedItems: PackagedItem[], ancestors: (string | PackagedItem)[] = [] ): any => { for (let pack of packagedItems) { if (pack.isOptional && !pack.item.packagedItems.length) { return ancestors.concat(pack); } const found = getParents( pack.item.packagedItems, ancestors.concat(pack.item.name) ); if (found) { return found; } } return undefined; }; console.log(getParents(product.packagedItems));
当前返回结果
[ "1#1", "2#1", { id: 6, isOptional: true, item: Object } ]
预期结果
const optionalTrue = [ { id: 1, isOptional: false, item: { name: "1#1", packagedItems: [ { id: 3, isOptional: false, item: { name: "2#1", packagedItems: [ { id: 6, isOptional: true, item: { name: "3#2", packagedItems: [] } } ] } } ] } }, { id: 2, isOptional: false, item: { name: "1#2", packagedItems: [ { id: 4, isOptional: false, item: { name: "2#2", packagedItems: [ { id: 7, isOptional: true, item: { name: "3#3", packagedItems: [] } }, { id: 8, isOptional: true, item: { name: "3#4", packagedItems: [] } } ] } } ] } } ]; const optionalFalse = [ { id: 1, isOptional: false, item: { name: "1#1", packagedItems: [ { id: 3, isOptional: false, item: { name: "2#1", packagedItems: [ { id: 5, isOptional: false, item: { name: "3#1", packagedItems: [] } } ] } } ] } } ];
解决方案代码
function splitByIsOptional(packagedItems) { const result = { optionalTrue: [], optionalFalse: [] }; for (const pack of packagedItems) { // 递归处理当前节点的子项 const childSplit = splitByIsOptional(pack.item.packagedItems); // 构建optionalTrue分支的节点副本 const trueNode = { ...pack, item: { ...pack.item } }; trueNode.item.packagedItems = childSplit.optionalTrue; // 若当前节点是可选,或子项存在可选节点,则加入optionalTrue数组 if (pack.isOptional || trueNode.item.packagedItems.length > 0) { result.optionalTrue.push(trueNode); } // 构建optionalFalse分支的节点副本(仅处理非可选节点) if (!pack.isOptional) { const falseNode = { ...pack, item: { ...pack.item } }; falseNode.item.packagedItems = childSplit.optionalFalse; // 若子项存在非可选节点,或当前节点无任何子项,则加入optionalFalse数组 if (falseNode.item.packagedItems.length > 0 || pack.item.packagedItems.length === 0) { result.optionalFalse.push(falseNode); } } } return result; } // 调用函数并获取结果 const { optionalTrue, optionalFalse } = splitByIsOptional(product.packagedItems); console.log('optionalTrue:', optionalTrue); console.log('optionalFalse:', optionalFalse);
代码说明
- 递归遍历:对每个层级的
packagedItems进行递归处理,确保覆盖所有嵌套层级 - 节点副本:每次处理节点时创建浅拷贝(避免修改原数据),并替换子项为对应分类的结果
- 分类逻辑:
optionalTrue:保留所有包含可选节点的完整路径,只要当前节点是可选,或子项中有可选节点,就保留该节点结构optionalFalse:仅保留非可选节点的路径,只有当前节点为非可选,且子项存在非可选节点或无任何子项时,才保留该节点结构
- 结构一致性:最终返回的两个数组结构与输入完全匹配,仅过滤掉不符合分类条件的子项
内容的提问来源于stack exchange,提问作者Sedana Yoga
相关产品推荐
相关产品推荐

