递归看似平常工作中很少用。实际上在一些特定业务场景中,用到的还蛮多。
场景一:对于多层嵌套数据结构,现在如何递归查找,直到找到id为某个具体指的对象?
这个常用在树形菜单中,当然一般我们会基于第三方组件库开发,第三方组件库提供了对应的事件可以得到当前的勾选项,如果需要我们自己手写。那递归就派上了用场:
实现一:
function digui(data, id) {
let value = null;
for (const item of data) {
if (item.id === id) {
value = item;
break;
}
if (item.children) {
const _value = digui(item.children, id);
if (_value) {
return _value;
}
}
}
return value;
}
console.log(digui(arr,999));
方法二:
let result = null
function handleDFS (arr, tid) {
let i = 0;
while (arr[i]) {
if (arr[i].id === tid) {
result = arr[i]
return
}
if (arr[i].children) handleDFS(arr[i].children, tid)
i++
}
return result
}
场景二:根据登录用户的权限code,动态过滤获取最后的树形菜单数据
这个场景非常普遍。用到递归可以事半功倍:
下面这个例子1.
let arr=["a","b","c","d","e"] //假设是后端给的权限code
let menu=[ //menu数组我们自己配置的菜单,menu中的children可能无限极//一级数据无code,但是children有code,保留有code并且code为arr里的选项 { name:"项目一", children:[ {name:"项目一(1)",code:"a" }, {name:"项目一(2)",code:"b" }, {name:"项目一(3)",code:"kkk" }, ]
},
{
name:"项目二",
children:[
{name:"项目一(1)",code:"yyy" },
{name:"项目一(2)",code:"d" },
{name:"项目一(3)",code:"" },
]
},
{
//一级数据有code,保留
name:"项目三",
code:"c"
},
{
name:"项目四",
code:"d"
},
//一级数据无code,剔除
{
name:"项目五",
},
]
最后要得到的结果如图:把所有满足要求的项平铺,如何实现?
let res=[
{name: '项目一(1)', code: 'a'}
{name: '项目一(2)', code: 'b'}
{name: '项目一(2)', code: 'd'}
{name: '项目三', code: 'c'}
{name: '项目四', code: 'd'}
]
解法:
let processUnit=(item)=>{
const {code}=item;
let result=null;
if(arr.includes(code)){
result=item
}
return result
}
let getMenu=(menu)=>{
let res=[];
menu.forEach(item=>{
const {children}=item;
if(Array.isArray(children)){
let data=getMenu(children);
res=[...res,...data]
}else{
let result=processUnit(item);
result&&(res.push(result))
}
})
return res
}
console.log("结果是:",getMenu(menu))
针对上面例子,实际场景中可能还会是另一种需求: 过滤出menu数组中含有code并且code存在于权限列表中的数据。比如
{ name:"项目一",
children:[
{name:"项目一(1)",code:"a" },
{name:"项目一(2)",code:"b" },
{name:"项目一(3)",code:"kkk" },
]
},
该项本身没有code,但是其子项“项目1”“项目2”的code都在权限列表中,所以该项应该被保留,同事剔除code伟"kkk"的第三项,这项本身过滤后应该变成这样:
{ name:"项目一",
children:[
{name:"项目一(1)",code:"a" },
{name:"项目一(2)",code:"b" },
]
},
其他项同理:
也就是menu数组最后的结果应该是:
{
//一级数据无code,但是children有code,保留有code并且code为arr里的选项
name:"项目一",
children:[
{name:"项目一(1)",code:"a" },
{name:"项目一(2)",code:"b" },
]
},
{
name:"项目二",
children:[
{name:"项目一(2)",code:"d" },
]
},
{
name:"项目三",
code:"c"
},
{
name:"项目四",
code:"d"
},
}
解法:
let getMenu=(menu)=>{
let res=[];
menu.forEach(item=>{
const {children}=item;
if(Array.isArray(children)){
let temp={...item,children:item.children.filter(k=>arr.includes(k.code))}
res=[...res,temp]
}else{
let result=processUnit(item);
result&&(res.push(result))
}
})
return res
}
let processUnit=(item)=>{
const {code}=item;
let result=null;
if(arr.includes(code)){
result=item
}
return result
}
let res=getMenu(menu)
console.log(res)
场景三:数据扁平:如果有子项,则抽出来平铺。最后返回一个平铺的数组
let arr=[
{
catalogCode: "0"
catalogName: "首页"
refImgNum: 131
},
{
catalogCode: "001"
catalogName: "卧室",
childCatalogs:
[
catalogCode: "015"
catalogName: "主卧"
refImgNum: 0
]
},
{
catalogCode: "014"
catalogName: "餐厅"
refImgNum: 1
}
]
//最后的结果为
arr=[
{
catalogCode: "0"
catalogName: "首页"
refImgNum: 131
},
{
catalogCode: "001"
catalogName: "卧室",
},
{
catalogCode: "015"
catalogName: "主卧"
refImgNum: 0
},
{
catalogCode: "014"
catalogName: "餐厅"
refImgNum: 1
}
]
解法:
const flatArr=(ary)=>{
let result = [];
for(let i = 0; i < ary.length; i++){
let item = ary[i];
result.push(item);
if (item.childCatalogs){
result.push(...flatArr(item.childCatalogs));
}
}
return result
}
export {flatArr}