如何在JavaScript中递归查询嵌套分类对象的顶级父类名称?
问题描述
现有嵌套结构的分类对象states,产品数组中存储了任意层级的分类ID,需要根据分类ID获取其顶级父类名称(例如ID为24时,要拿到"Category #1")。当前使用的filter函数只能查询一级分类,无法处理深层分类。
现有分类对象结构
{ "1": { "id": "1", "name": "Category #1", "hasChild": "Y", "count": "0", "parentId": null, "link": "/catalog/", "subcategories": [ { "id": "21", "name": "Subcategory #1", "hasChild": "Y", "count": "0", "parentId": "1", "link": "/catalog/", "subcategories": [ { "id": "24", "name": "subsubcategory #1", "hasChild": "Y", "count": "1", "parentId": "21", "link": "/catalog/", "subcategories": [], }, { "id": "25", "name": "subsubcategory #2", "hasChild": "Y", "count": "0", "parentId": "21", "link": "/catalog/", "subcategories": [], } ], }, { "id": "22", "name": "Subcategory #2", "hasChild": "Y", "count": "0", "parentId": "1", "link": "/catalog/", }, { "id": "23", "name": "Subcategory #3", "hasChild": "Y", "count": "0", "parentId": "1", "link": "/catalog/", } ], }, "2": { "id": "2", "name": "Category #2", "hasChild": "Y", "count": "0", "parentId": null, "link": "/catalog/", "subcategories": [ // 子分类省略 ], }, }
产品关联分类ID示例
["24", "22", "2"]
现有函数问题
现有filter函数无法查询深层分类,核心问题是代码中判断子分类的字段为i.children,但实际分类结构中子分类字段是subcategories,导致递归无法进入深层节点:
function filter(item, search, textKey) { let result = [] const _filter = (item, search, textKey) => { for (const i of item) { if (i[textKey].indexOf(search) !== -1) { result = [...result, { name: i.name, id: i.id, parentId: i.parentId }] } // 错误:实际子分类字段是subcategories,不是children i.children ? _filter(i.children, search, textKey) : null } } _filter(item, search, textKey) return result }
测试结果:
console.log(filter(Object.values(states), '24', 'id')) // 无结果 console.log(filter(Object.values(states), '2', 'id')) // 正常返回一级分类
解决方案
第一步:修复递归查询,找到目标分类节点
将子分类字段修正为subcategories,同时优化逻辑,找到目标后可提前终止递归:
// 递归查找任意层级的分类节点 function findCategoryNode(categories, targetId) { for (const category of categories) { if (category.id === targetId) { return category; } // 递归遍历子分类 if (category.subcategories && category.subcategories.length > 0) { const found = findCategoryNode(category.subcategories, targetId); if (found) return found; } } return null; }
第二步:根据节点追溯顶级父类
拿到目标节点后,通过parentId向上遍历,直到找到parentId为null的顶级分类:
// 根据分类ID获取顶级父类名称 function getTopParentCategoryName(states, targetId) { // 先找到目标分类节点 const targetNode = findCategoryNode(Object.values(states), targetId); if (!targetNode) return null; // 向上追溯顶级父类 let currentNode = targetNode; while (currentNode.parentId !== null) { currentNode = states[currentNode.parentId]; } return currentNode.name; }
使用示例
// 测试ID为24的情况 console.log(getTopParentCategoryName(states, '24')); // 输出 "Category #1" // 测试ID为22的情况 console.log(getTopParentCategoryName(states, '22')); // 输出 "Category #1" // 测试ID为2的情况 console.log(getTopParentCategoryName(states, '2')); // 输出 "Category #2"
可选优化:扁平化分类提升查询效率
如果需要频繁查询,可提前将所有分类节点扁平化存储,减少递归开销:
// 扁平化所有分类节点到对象中 function flattenCategories(states) { const flatMap = {}; function traverse(categories) { for (const cat of categories) { flatMap[cat.id] = cat; if (cat.subcategories && cat.subcategories.length > 0) { traverse(cat.subcategories); } } } traverse(Object.values(states)); return flatMap; } // 提前生成扁平化映射 const flatCategories = flattenCategories(states); // 优化后的顶级父类查询 function getTopParentNameOptimized(flatCategories, states, targetId) { let currentNode = flatCategories[targetId]; if (!currentNode) return null; while (currentNode.parentId !== null) { currentNode = states[currentNode.parentId]; } return currentNode.name; } // 使用优化后的函数 console.log(getTopParentNameOptimized(flatCategories, states, '24')); // 输出 "Category #1"
内容的提问来源于stack exchange,提问作者Timur Iskakov
相关产品推荐
相关产品推荐

