整理了一系列的JavaScript樹(shù)操作方法,不用再一遍又一遍的百度了
前言
樹(shù)結(jié)構(gòu)的數(shù)據(jù)操作對(duì)于一個(gè)開(kāi)發(fā)者來(lái)說(shuō)是一個(gè)必備的技能。在實(shí)際的業(yè)務(wù)開(kāi)發(fā)中,我們也會(huì)遇到許多樹(shù)結(jié)構(gòu)的體現(xiàn),比如最常見(jiàn)的地域樹(shù),以及企業(yè)結(jié)構(gòu)樹(shù)、校級(jí)組織樹(shù)等等。
下面整理了一系列的關(guān)于JavaScript樹(shù)的操作方法,結(jié)合示例,相信大家在實(shí)際開(kāi)發(fā)工作中或多或少都會(huì)用到。
數(shù)組扁平化
示例
const?arr?=?[1,?[2,?[3,?4]],?5,?[6]];
方法
1、遞歸
const?flatten?=?(arr)?=>?{
????let?res?=?[];
????arr.map(item?=>?{
????????if(Array.isArray(item))?{
????????????res?=?res.concat(flatten(item));
????????}?else?{
????????????res.push(item);
????????}
????});
????return?res;
}
2、reduce
const?flatten?=?(arr)?=>?{
????return?arr.reduce((result,?item)=>?{
????????return?result.concat(Array.isArray(item)???flatten(item)?:?item);
????},?[]);
}
3、flat
const?flatten?=?(arr)?=>?{
????return?arr.flat(Infinity)
}
運(yùn)行結(jié)果
const?result?=?flatten(arr);
console.log(result);
//?運(yùn)行結(jié)果
[1,?2,?3,?4,?5,?6]
數(shù)組轉(zhuǎn)樹(shù)形結(jié)構(gòu)
示例
const?arr?=?[
????{
????????name:?'小明',
????????id:?1,
????????pid:?0,
????},
????{
????????name:?'小花',
????????id:?11,
????????pid:?1,
????},
????{
????????name:?'小華',
????????id:?111,
????????pid:?11,
????},
????{
????????name:?'小李',
????????id:?112,
????????pid:?11,
????},
????{
????????name:?'小紅',
????????id:?12,
????????pid:?1,
????},
????{
????????name:?'小王',
????????id:?2,
????????pid:?0,
????},
????{
????????name:?'小林',
????????id:?21,
????????pid:?2,
????},
????{
????????name:?'小李',
????????id:?22,
????????pid:?2,
????}
]
方法
1、非遞歸
?const?arrayToTree?=?(arr)?=>?{
????let?result?=?[];
????if?(!Array.isArray(arr)?||?arr.length?===?0)?{
????????return?result
????}
????let?map?=?{};
????arr.forEach(item?=>?map[item.id]?=?item);
????arr.forEach(item?=>?{
????????const?parent?=?map[item.pid];
????????if(parent){
????????????(parent.children?||?(parent.children=[])).push(item);
????????}?else?{
????????????result.push(item);
????????}
????})
????return?result
}
2、遞歸
const?arrayToTree?=?(arr,?pid)?=>?{
????let?res?=?[];
????arr.forEach(item?=>?{
????????if(item.pid?===?pid){
????????????let?itemChildren?=?arrayToTree(arr,item.id);
????????????if(itemChildren.length)?{
????????????????item.children?=?itemChildren;
????????????}
????????????res.push(item);
????????}
????});
????return?res;
}
運(yùn)行結(jié)果
//?const?result?=?arrayToTree(arr);
const?result?=?arrayToTree(arr,?0);
console.log(result);
//?運(yùn)行結(jié)果
[
????{
????????"name":?"小明",
????????"id":?1,
????????"pid":?0,
????????"children":?[
????????????{
????????????????"name":?"小花",
????????????????"id":?11,
????????????????"pid":?1,
????????????????"children":?[
????????????????????{
????????????????????????"name":?"小華",
????????????????????????"id":?111,
????????????????????????"pid":?11
????????????????????},
????????????????????{
????????????????????????"name":?"小李",
????????????????????????"id":?112,
????????????????????????"pid":?11
????????????????????}
????????????????]
????????????},
????????????{
????????????????"name":?"小紅",
????????????????"id":?12,
????????????????"pid":?1
????????????}
????????]
????},
????{
????????"name":?"小王",
????????"id":?2,
????????"pid":?0,
????????"children":?[
????????????{
????????????????"name":?"小林",
????????????????"id":?21,
????????????????"pid":?2
????????????},
????????????{
????????????????"name":?"小李",
????????????????"id":?22,
????????????????"pid":?2
????????????}
????????]
????}
]
樹(shù)形結(jié)構(gòu)轉(zhuǎn)數(shù)組(扁平化)
示例
const?tree?=?[
????{
????????name:?'小明',
????????id:?1,
????????pid:?0,
????????children:?[
????????????{
????????????????name:?'小花',
????????????????id:?11,
????????????????pid:?1,
????????????????children:?[
????????????????????{
????????????????????????name:?'小華',
????????????????????????id:?111,
????????????????????????pid:?11,
????????????????????},
????????????????????{
????????????????????????name:?'小李',
????????????????????????id:?112,
????????????????????????pid:?11,
????????????????????}
????????????????]
????????????},
????????????{
????????????????name:?'小紅',
????????????????id:?12,
????????????????pid:?1,
????????????}
????????]
????},
????{
????????name:?'小王',
????????id:?2,
????????pid:?0,
????????children:?[
????????????{
????????????????name:?'小林',
????????????????id:?21,
????????????????pid:?2,
????????????},
????????????{
????????????????name:?'小李',
????????????????id:?22,
????????????????pid:?2,
????????????}
????????]
????}
]
方法
1、深度優(yōu)先遍歷
const?treeToArray?=?(tree)?=>?{
????let?stack?=?tree,
????????result?=?[];
????while(stack.length?!==?0){
????????let?pop?=?stack.pop();
????????result.push({
????????????id:?pop.id,
????????????name:?pop.name,
????????????pid:?pop.pid
????????})
????????let?children?=?pop.children
????????if(children){
????????????for(let?i?=?children.length-1;?i?>=0;?i--){
????????????????stack.push(children[i])
????????????}
????????}
????}
????return?result
}
2、廣度優(yōu)先遍歷
const?treeToArray?=?(tree)?=>?{
????let?queue?=?tree,
????????result?=?[];
????while(queue.length?!==?0){
????????let?shift?=?queue.shift();
????????result.push({
????????????id:?shift.id,
????????????name:?shift.name,
????????????pid:?shift.pid
????????})
????????let?children?=?shift.children
????????if(children){
????????????for(let?i?=?0;?i?????????????????queue.push(children[i])
????????????}
????????}
????}
????return?result
}
3、不用考慮除children外的其他屬性
const?treeToArray?=?(source)=>{
????let?res?=?[]
????source.forEach(item=>{
????????res.push(item)
????????item.children?&&?res.push(...treeToArray(item.children))
????})
????return?res.map((item)?=>?{
????????if?(item.children)?{
????????????delete?item.children
????????}
????????return?item
????})
}
運(yùn)行結(jié)果
const?result?=?treeToArray(tree);
console.log(result);
//?運(yùn)行結(jié)果
[
????{
????????"name":?"小明",
????????"id":?1,
????????"pid":?0
????},
????{
????????"name":?"小花",
????????"id":?11,
????????"pid":?1
????},
????{
????????"name":?"小華",
????????"id":?111,
????????"pid":?11
????},
????{
????????"name":?"小李",
????????"id":?112,
????????"pid":?11
????},
????{
????????"name":?"小紅",
????????"id":?12,
????????"pid":?1
????},
????{
????????"name":?"小王",
????????"id":?2,
????????"pid":?0
????},
????{
????????"name":?"小林",
????????"id":?21,
????????"pid":?2
????},
????{
????????"name":?"小李",
????????"id":?22,
????????"pid":?2
????}
]
樹(shù)篩選,保留符合條件的數(shù)據(jù)并返回樹(shù)結(jié)構(gòu)
示例
const?tree?=?[
????{
????????name:?'小明',
????????id:?1,
????????pid:?0,
????????show:?true,
????????children:?[
????????????{
????????????????name:?'小花',
????????????????id:?11,
????????????????pid:?1,
????????????????show:?true,
????????????????children:?[
????????????????????{
????????????????????????name:?'小華',
????????????????????????id:?111,
????????????????????????pid:?11,
????????????????????},
????????????????????{
????????????????????????name:?'小李',
????????????????????????id:?112,
????????????????????????pid:?11,
????????????????????????show:?true,
????????????????????}
????????????????]
????????????},
????????????{
????????????????name:?'小紅',
????????????????id:?12,
????????????????pid:?1,
????????????}
????????]
????},
????{
????????name:?'小王',
????????id:?2,
????????pid:?0,
????????show:?true,
????????children:?[
????????????{
????????????????name:?'小林',
????????????????id:?21,
????????????????pid:?2,
????????????},
????????????{
????????????????name:?'小李',
????????????????id:?22,
????????????????pid:?2,
????????????}
????????]
????}
]
方法
篩選出show為true數(shù)據(jù)
const?filterTreeByFunc?=?(tree,?func)?=>?{
????if?(!Array.isArray(tree)?||?tree.length?===?0)?{
????????return?[]
????}
????return?tree.filter(item?=>?{
????????item.children?=?item.children?&&?filterTreeByFunc(item.children,?func)
????????return?func(item)?||?(item.children?&&?item.children.length)
????})
}
const?func?=?(item)?=>?{
????return?item.show?===?true
}
運(yùn)行結(jié)果
const?result?=?filterTreeByFunc(tree,?func);
console.log(result);
//?運(yùn)行結(jié)果
[
????{
????????"name":?"小明",
????????"id":?1,
????????"pid":?0,
????????"show":?true,
????????"children":?[
????????????{
????????????????"name":?"小花",
????????????????"id":?11,
????????????????"pid":?1,
????????????????"show":?true,
????????????????"children":?[
????????????????????{
????????????????????????"name":?"小李",
????????????????????????"id":?112,
????????????????????????"pid":?11,
????????????????????????"show":?true
????????????????????}
????????????????]
????????????}
????????]
????},
????{
????????"name":?"小王",
????????"id":?2,
????????"pid":?0,
????????"show":?true,
????????"children":?[]
????}
]
查找某一節(jié)點(diǎn)在樹(shù)中路徑
示例
const?tree?=?[
????{
????????name:?'小明',
????????id:?1,
????????pid:?0,
????????children:?[
????????????{
????????????????name:?'小花',
????????????????id:?11,
????????????????pid:?1,
????????????????children:?[
????????????????????{
????????????????????????name:?'小華',
????????????????????????id:?111,
????????????????????????pid:?11,
????????????????????},
????????????????????{
????????????????????????name:?'小李',
????????????????????????id:?112,
????????????????????????pid:?11,
????????????????????}
????????????????]
????????????},
????????????{
????????????????name:?'小紅',
????????????????id:?12,
????????????????pid:?1,
????????????}
????????]
????},
????{
????????name:?'小王',
????????id:?2,
????????pid:?0,
????????children:?[
????????????{
????????????????name:?'小林',
????????????????id:?21,
????????????????pid:?2,
????????????},
????????????{
????????????????name:?'小李',
????????????????id:?22,
????????????????pid:?2,
????????????}
????????]
????}
]
方法
const?getNodePath?=?(tree,?id)?=>?{
????if?(!Array.isArray(tree)?||?tree.length?===?0)?{
????????return?[]
????}
????const?path?=?[]
????const?treeFindPath?=?(tree,?id,?path)?=>?{
????????for?(const?item?of?tree)?{
????????????path.push(item.id);
????????????if?(item.id?===?id)?{
????????????????return?path
????????????}
????????????if?(item.children)?{
????????????????const?findChildren?=?treeFindPath(item.children,id,?path);
????????????????if?(findChildren.length)?{
????????????????????return?findChildren;
????????????????}
????????????}
????????????path.pop();
????????}
????????return?[];
????}
????return?treeFindPath(tree,?id,?path)
}
運(yùn)行結(jié)果
const?result?=?getNodePath(tree,?112);
console.log(result);
//?運(yùn)行結(jié)果
[1,?11,?112]
模糊查詢(xún)樹(shù)
示例
const?tree?=?[
????{
????????name:?'小明前端專(zhuān)家',
????????id:?1,
????????pid:?0,
????????children:?[
????????????{
????????????????name:?'小花前端程序媛',
????????????????id:?11,
????????????????pid:?1,
????????????????children:?[
????????????????????{
????????????????????????name:?'小華劃水運(yùn)動(dòng)員',
????????????????????????id:?111,
????????????????????????pid:?11,
????????????????????},
????????????????????{
????????????????????????name:?'小李摸魚(yú)運(yùn)動(dòng)員',
????????????????????????id:?112,
????????????????????????pid:?11,
????????????????????}
????????????????]
????????????},
????????????{
????????????????name:?'小紅摸魚(yú)程序員',
????????????????id:?12,
????????????????pid:?1,
????????????}
????????]
????},
????{
????????name:?'小王內(nèi)卷王',
????????id:?2,
????????pid:?0,
????????children:?[
????????????{
????????????????name:?'小林摸魚(yú)王',
????????????????id:?21,
????????????????pid:?2,
????????????},
????????????{
????????????????name:?'小李后端程序員',
????????????????id:?22,
????????????????pid:?2,
????????????}
????????]
????}
]
方法
const?fuzzyQueryTree?=?(arr,?value)?=>?{
????if?(!Array.isArray(arr)?||?arr.length?===?0)?{
????????return?[]
????}
????let?result?=?[];
????arr.forEach(item?=>?{
????????if?(item.name.indexOf(value)?>?-1)?{
????????????const?children?=?fuzzyQueryTree(item.children,?value);
????????????const?obj?=?{?...item,?children?}
????????????result.push(obj);
????????}?else?{
????????????if?(item.children?&&?item.children.length?>?0)?{
????????????????const?children?=?fuzzyQueryTree(item.children,?value);
????????????????const?obj?=?{?...item,?children?}
????????????????if?(children?&&?children.length?>?0)?{
????????????????????result.push(obj);
????????????????}
????????????}
????????}
????});
????return?result;
};
運(yùn)行結(jié)果
const?result?=?fuzzyQueryTree(tree,'程序');
console.log(result);
//?運(yùn)行結(jié)果
[
????{
????????"name":?"小明前端專(zhuān)家",
????????"id":?1,
????????"pid":?0,
????????"children":?[
????????????{
????????????????"name":?"小花前端程序媛",
????????????????"id":?11,
????????????????"pid":?1,
????????????????"children":?[]
????????????},
????????????{
????????????????"name":?"小紅摸魚(yú)程序員",
????????????????"id":?12,
????????????????"pid":?1,
????????????????"children":?[]
????????????}
????????]
????},
????{
????????"name":?"小王內(nèi)卷王",
????????"id":?2,
????????"pid":?0,
????????"children":?[
????????????{
????????????????"name":?"小李后端程序員",
????????????????"id":?22,
????????????????"pid":?2,
????????????????"children":?[]
????????????}
????????]
????}
]
樹(shù)節(jié)點(diǎn)添加屬性
示例
const?tree?=?[
????{
????????name:?'小明',
????????id:?1,
????????pid:?0,
????????children:?[
????????????{
????????????????name:?'小花',
????????????????id:?11,
????????????????pid:?1,
????????????????children:?[
????????????????????{
????????????????????????name:?'小華',
????????????????????????id:?111,
????????????????????????pid:?11,
????????????????????},
????????????????????{
????????????????????????name:?'小李',
????????????????????????id:?112,
????????????????????????pid:?11,
????????????????????}
????????????????]
????????????},
????????????{
????????????????name:?'小紅',
????????????????id:?12,
????????????????pid:?1,
????????????}
????????]
????},
????{
????????name:?'小王',
????????id:?2,
????????pid:?0,
????????children:?[
????????????{
????????????????name:?'小林',
????????????????id:?21,
????????????????pid:?2,
????????????},
????????????{
????????????????name:?'小李',
????????????????id:?22,
????????????????pid:?2,
????????????}
????????]
????}
]
方法
const?addAttrToNodes?=?(tree)?=>?{
????tree.forEach((item)?=>?{
????????item.title?=?'新生代農(nóng)民工'
????????if?(item.children?&&?item.children.length?>?0)?{
????????????addAttrToNodes(item.children)
????????}
????})
????return?tree
}
運(yùn)行結(jié)果
const?result?=?addAttrToNodes(tree);
console.log(result);
//?運(yùn)行結(jié)果
[
????{
????????"name":?"小明",
????????"id":?1,
????????"pid":?0,
????????"children":?[
????????????{
????????????????"name":?"小花",
????????????????"id":?11,
????????????????"pid":?1,
????????????????"children":?[
????????????????????{
????????????????????????"name":?"小華",
????????????????????????"id":?111,
????????????????????????"pid":?11,
????????????????????????"title":?"新生代農(nóng)民工"
????????????????????},
????????????????????{
????????????????????????"name":?"小李",
????????????????????????"id":?112,
????????????????????????"pid":?11,
????????????????????????"title":?"新生代農(nóng)民工"
????????????????????}
????????????????],
????????????????"title":?"新生代農(nóng)民工"
????????????},
????????????{
????????????????"name":?"小紅",
????????????????"id":?12,
????????????????"pid":?1,
????????????????"title":?"新生代農(nóng)民工"
????????????}
????????],
????????"title":?"新生代農(nóng)民工"
????},
????{
????????"name":?"小王",
????????"id":?2,
????????"pid":?0,
????????"children":?[
????????????{
????????????????"name":?"小林",
????????????????"id":?21,
????????????????"pid":?2,
????????????????"title":?"新生代農(nóng)民工"
????????????},
????????????{
????????????????"name":?"小李",
????????????????"id":?22,
????????????????"pid":?2,
????????????????"title":?"新生代農(nóng)民工"
????????????}
????????],
????????"title":?"新生代農(nóng)民工"
????}
]
樹(shù)節(jié)點(diǎn)刪除屬性
示例
這里直接使用上面——「樹(shù)形結(jié)構(gòu)節(jié)點(diǎn)添加屬性」的運(yùn)行結(jié)果
方法
const?removeAttrFromNode?=?(tree)?=>?{
????tree.forEach((item)?=>?{
????????delete?item.title
????????if?(item.children?&&?item.children.length?>?0)?{
????????????removeAttrFromNode(item.children)
????????}
????})
????return?tree
}
運(yùn)行結(jié)果
const?result?=?removeAttrFromNode(tree);
console.log(result);
//?運(yùn)行結(jié)果
[
????{
????????"name":?"小明",
????????"id":?1,
????????"pid":?0,
????????"children":?[
????????????{
????????????????"name":?"小花",
????????????????"id":?11,
????????????????"pid":?1,
????????????????"children":?[
????????????????????{
????????????????????????"name":?"小華",
????????????????????????"id":?111,
????????????????????????"pid":?11
????????????????????},
????????????????????{
????????????????????????"name":?"小李",
????????????????????????"id":?112,
????????????????????????"pid":?11
????????????????????}
????????????????]
????????????},
????????????{
????????????????"name":?"小紅",
????????????????"id":?12,
????????????????"pid":?1
????????????}
????????]
????},
????{
????????"name":?"小王",
????????"id":?2,
????????"pid":?0,
????????"children":?[
????????????{
????????????????"name":?"小林",
????????????????"id":?21,
????????????????"pid":?2
????????????},
????????????{
????????????????"name":?"小李",
????????????????"id":?22,
????????????????"pid":?2
????????????}
????????]
????}
]
刪除樹(shù)中的空children
示例
const?tree?=?[
????{
????????name:?'小明',
????????id:?1,
????????pid:?0,
????????children:?[
????????????{
????????????????name:?'小花',
????????????????id:?11,
????????????????pid:?1,
????????????????children:?[
????????????????????{
????????????????????????name:?'小華',
????????????????????????id:?111,
????????????????????????pid:?11,
????????????????????},
????????????????????{
????????????????????????name:?'小李',
????????????????????????id:?112,
????????????????????????pid:?11,
????????????????????????children:?[]
????????????????????}
????????????????]
????????????},
????????????{
????????????????name:?'小紅',
????????????????id:?12,
????????????????pid:?1,
????????????????children:?[]
????????????}
????????]
????},
????{
????????name:?'小王',
????????id:?2,
????????pid:?0,
????????children:?[
????????????{
????????????????name:?'小林',
????????????????id:?21,
????????????????pid:?2,
????????????},
????????????{
????????????????name:?'小李',
????????????????id:?22,
????????????????pid:?2,
????????????????children:?[]
????????????}
????????]
????}
]
方法
const?removeEmptyChildren?=?(tree)?=>?{
????tree.forEach((item)?=>?{
????????if?(item.children?&&?item.children.length?===0)?{
????????????delete?item.children
????????}?else?if?(item.children?&&?item.children.length?>?0)?{
????????????removeEmptyChildren(item.children)
????????}
????})
????return?tree
}
運(yùn)行結(jié)果
const?result?=?removeEmptyChildren(tree);
console.log(result);
//?運(yùn)行結(jié)果
[
????{
????????"name":?"小明",
????????"id":?1,
????????"pid":?0,
????????"children":?[
????????????{
????????????????"name":?"小花",
????????????????"id":?11,
????????????????"pid":?1,
????????????????"children":?[
????????????????????{
????????????????????????"name":?"小華",
????????????????????????"id":?111,
????????????????????????"pid":?11
????????????????????},
????????????????????{
????????????????????????"name":?"小李",
????????????????????????"id":?112,
????????????????????????"pid":?11
????????????????????}
????????????????]
????????????},
????????????{
????????????????"name":?"小紅",
????????????????"id":?12,
????????????????"pid":?1
????????????}
????????]
????},
????{
????????"name":?"小王",
????????"id":?2,
????????"pid":?0,
????????"children":?[
????????????{
????????????????"name":?"小林",
????????????????"id":?21,
????????????????"pid":?2
????????????},
????????????{
????????????????"name":?"小李",
????????????????"id":?22,
????????????????"pid":?2
????????????}
????????]
????}
]
獲取樹(shù)中所有的葉子節(jié)點(diǎn)
示例
const?tree?=?[
????{
????????name:?'小明',
????????id:?1,
????????pid:?0,
????????children:?[
????????????{
????????????????name:?'小花',
????????????????id:?11,
????????????????pid:?1,
????????????????children:?[
????????????????????{
????????????????????????name:?'小華',
????????????????????????id:?111,
????????????????????????pid:?11,
????????????????????},
????????????????????{
????????????????????????name:?'小李',
????????????????????????id:?112,
????????????????????????pid:?11,
????????????????????}
????????????????]
????????????},
????????????{
????????????????name:?'小紅',
????????????????id:?12,
????????????????pid:?1,
????????????}
????????]
????},
????{
????????name:?'小王',
????????id:?2,
????????pid:?0,
????????children:?[
????????????{
????????????????name:?'小林',
????????????????id:?21,
????????????????pid:?2,
????????????},
????????????{
????????????????name:?'小李',
????????????????id:?22,
????????????????pid:?2,
????????????}
????????]
????}
]
方法
const?getAllLeaf?=?(tree)?=>?{
????const?result?=?[]
????const?getLeaf?=?(tree)?=>?{
????????tree.forEach((item)?=>?{
????????????if?(!item.children)?{
????????????????result.push(item)
????????????}?else?{
????????????????getLeaf(item.children)
????????????}
????????})
????}
????getLeaf(tree)
????return?result
}
運(yùn)行結(jié)果
const?result?=?getAllLeaf(tree);
console.log(result);
//?運(yùn)行結(jié)果
[
????{
????????"name":?"小華",
????????"id":?111,
????????"pid":?11
????},
????{
????????"name":?"小李",
????????"id":?112,
????????"pid":?11
????},
????{
????????"name":?"小紅",
????????"id":?12,
????????"pid":?1
????},
????{
????????"name":?"小林",
????????"id":?21,
????????"pid":?2
????},
????{
????????"name":?"小李",
????????"id":?22,
????????"pid":?2
????}
]
參考
https://wintc.top/article/20
https://www.cnblogs.com/mengff/p/13142128.html
https://blog.csdn.net/susuzhe123/article/details/95353403
https://blog.csdn.net/web_yueqiang/article/details/89483971
最后
本文整理了一系列的關(guān)于JavaScript樹(shù)的操作方法,相當(dāng)于平時(shí)的一個(gè)總結(jié)。大家可以拿來(lái)即用,或者根據(jù)實(shí)際的業(yè)務(wù)進(jìn)行參考修改。
如果大家有更好的實(shí)現(xiàn)方式,或者自己在開(kāi)發(fā)中遇到的,但是上面沒(méi)有涉及到的,歡迎提出來(lái),大家一起討論一起進(jìn)步~
