js中操作樹結構的資料
阿新 • • 發佈:2020-12-13
技術標籤:javascriptjavascript
現在前端操作樹的資料結構還是挺常見的,我在這裡總結一下js樹形結構常見的操作方式。我也把它做成了工具類庫,github地址。
一.遍歷樹結構的方式
假設現在有如下的資料結構,我們需要去找到樹結構的中某個物件的資料,常見的操作方式應該是用遞迴進行遍歷查詢,我認為這是最基本應該能想到的。
let tree = [
{
id: '1',
title: '節點1',
children: [
{
id: '1-1',
title: '節點1-1'
},
{
id: '1-2',
title: '節點1-2'
}
]
},
{
id: '2',
title: '節點2',
children: [
{
id: '2-1',
title: '節點2-1'
}
]
}
]
const recursion = (cityData, id) => {
if (!cityData || !cityData.length) {
return
}
//先循壞cityData
for (let i = 0; i < cityData.length; i++) {
const childs = cityData[i].children;
// console.log(cityData[i].id)
if (cityData[i].id === id) {
result = cityData[i]
}
if (childs && childs.length > 0) {
recursion(childs, id);
}
}
return result
}
console.log(recursion(cityData, "2-1"))// {title:"節點2-1",id:"2-1"}
也可以使用棧迭代的方式,這也是我個人平時遍歷樹常用的一種方式。
let result = ''
const range = (cityData, id) => {
if (!cityData || !cityData.length) return;
// 定義一個數據棧
let stack = [];
let item = null;
//先將第一層節點放入棧
for (var i = 0, len = cityData.length; i < len; i++) {
stack.push(cityData[i]);
}
while (stack.length) {
// 將資料棧的第一個取出來
item = stack.shift();
// 如果符合就賦值給result
if (item.id === id) {
result = item
}
//如果該節點有子節點,繼續新增進入棧底
if (item.children && item.children.length) {
stack = stack.concat(item.children);
}
}
return result
};
console.log(range(cityData, "2-1"))// {title:"節點2-1",id:"2-1"}
二.操作樹的增刪改查
1.列表與樹的相互裝換
列表的資料結構會在節點資訊中給定當前父元素的pid,然後可以通過pid進行相互關聯進而轉換成為一棵樹結構。
let list = [
{id: "1",title: "節點1",parentId: "",},
{id: "1-1",title: "節點1-1",parentId: "1"},
{id: "2",title: "節點2",parentId: ""},
{id: "2-1", title: "節點2-1", parentId: "2"},
{id: "1-2", title: "節點1-2", parentId: "1"},
{id: "1-3", title: "節點1-3", parentId: "1"},
{id: "2-3", title: "節點2-3", parentId: "2"},
{title: "節點2-3-1", parentId: "2-3",id: "2-3-1"}
];
在tree.js中
class Tree {
constructor(config = {}) {
this.defaultConfig = {
id: "id",
children: "children",
pid: "pid"
};
this.config = Object.assign(this.defaultConfig, config);
}
// list=>tree 列表轉換成為樹
listToTree(list) {
let info = list.reduce((map, node) => {
if (!map[node[this.config.id]]) {
map[node[this.config.id]] = node;
node.children = [];
}
return map;
}, {});
return list.filter(v => {
if (info[v[this.config.pId]]) {
info[v[this.config.pId]].children.push(v);
}
return !v[[this.config.pId]];
});
}
}
export default Tree;
這裡我們利用了物件key的唯一性,把info與當前元素id建立起來一個對映關係,那麼它映射出來的結構如下:
然後通過filter遍歷list中的元素,判斷當前當前元素的pid是否存在於info集合中,如果存在,則說明當前元素為info中某項的子元素。然後再過濾出pid為空的元素。
呼叫:
let list = [
{
id: "1",
title: "節點1",
parentId: "",
},
{
id: "1-1",
title: "節點1-1",
parentId: "1"
},
{
id: "2",
title: "節點2",
parentId: ""
},
{id: "2-1", title: "節點2-1", parentId: "2"},
{id: "1-2", title: "節點1-2", parentId: "1"},
{id: "1-3", title: "節點1-3", parentId: "1"
},
{id: "2-3", title: "節點2-3", parentId: "2"},
{title: "節點2-3-1", parentId: "2-3", id: "2-3-1"}
];
console.log(new Tree({id: "id", pId: "parentId", children: "children"}).listToTree(list))
2.樹轉換成為列表
treeToList(tree) {
const { children } = this.config; const result = [...tree];
for (let i = 0; i < result.length; i++) {
if (!result[i][children]) {continue;}
result.splice(i + 1, 0, ...result[i][children]);
}
return result;
}
樹變成列表你可以這樣理解,遍歷當前項資料,判斷children.length是否大於0,如果大於則把它的children中的每個元素都排進列表中即可,由於此時result長度發生了變化,那麼它的迴圈也會繼續走下去。如果children.length === 0 則說明他沒有子節點則不需要任何的操作。