JavaScript 實(shí)現(xiàn)普通數(shù)組數(shù)據(jù)轉(zhuǎn)化為樹形數(shù)據(jù)結(jié)構(gòu)的步驟說明
在 JavaScript 中,將普通數(shù)組數(shù)據(jù)轉(zhuǎn)化為樹形結(jié)構(gòu)的數(shù)據(jù)是一個(gè)常見的任務(wù),特別是在處理層級(jí)數(shù)據(jù)(例如分類、組織結(jié)構(gòu)等)時(shí)。下面是一個(gè)詳細(xì)的步驟說明,展示如何將一個(gè)扁平的數(shù)組轉(zhuǎn)化為樹形數(shù)據(jù)結(jié)構(gòu)。
示例數(shù)據(jù)
假設(shè)我們有以下的扁平數(shù)組,每個(gè)元素代表一個(gè)節(jié)點(diǎn),并且每個(gè)節(jié)點(diǎn)包含一個(gè) id 和一個(gè) parentId,parentId 用于表示父節(jié)點(diǎn)的關(guān)系:
const data = [
{ id: 1, name: 'Root', parentId: null },
{ id: 2, name: 'Child 1', parentId: 1 },
{ id: 3, name: 'Child 2', parentId: 1 },
{ id: 4, name: 'Grandchild 1', parentId: 2 },
{ id: 5, name: 'Grandchild 2', parentId: 2 },
];目標(biāo)
將上述數(shù)據(jù)轉(zhuǎn)化為以下樹形結(jié)構(gòu):
[
{
id: 1,
name: 'Root',
children: [
{
id: 2,
name: 'Child 1',
children: [
{ id: 4, name: 'Grandchild 1', children: [] },
{ id: 5, name: 'Grandchild 2', children: [] }
]
},
{
id: 3,
name: 'Child 2',
children: []
}
]
}
]實(shí)現(xiàn)步驟
創(chuàng)建一個(gè)空對(duì)象來存儲(chǔ)每個(gè)節(jié)點(diǎn)的引用:
const nodeMap = {};遍歷數(shù)組,初始化節(jié)點(diǎn)并填充 nodeMap:
data.forEach(item => {
nodeMap[item.id] = { ...item, children: [] };
});這樣每個(gè)節(jié)點(diǎn)都被映射到 nodeMap 中,并且每個(gè)節(jié)點(diǎn)都有一個(gè) children 屬性用于存儲(chǔ)子節(jié)點(diǎn)。
遍歷數(shù)組,將每個(gè)節(jié)點(diǎn)添加到其父節(jié)點(diǎn)的 children 中:
data.forEach(item => {
if (item.parentId) {
// 如果節(jié)點(diǎn)有父節(jié)點(diǎn),將其添加到父節(jié)點(diǎn)的 children 中
const parent = nodeMap[item.parentId];
if (parent) {
parent.children.push(nodeMap[item.id]);
}
}
});提取根節(jié)點(diǎn):
根節(jié)點(diǎn)是 parentId 為 null 的節(jié)點(diǎn)。我們可以從 nodeMap 中找出這些節(jié)點(diǎn)。
const tree = Object.values(nodeMap).filter(node => node.parentId === null);
完整代碼示例
以下是將上述步驟整合在一起的完整代碼:
const data = [
{ id: 1, name: 'Root', parentId: null },
{ id: 2, name: 'Child 1', parentId: 1 },
{ id: 3, name: 'Child 2', parentId: 1 },
{ id: 4, name: 'Grandchild 1', parentId: 2 },
{ id: 5, name: 'Grandchild 2', parentId: 2 },
];
function buildTree(data) {
const nodeMap = {};
// 初始化 nodeMap
data.forEach(item => {
nodeMap[item.id] = { ...item, children: [] };
});
// 構(gòu)建樹形結(jié)構(gòu)
data.forEach(item => {
if (item.parentId) {
const parent = nodeMap[item.parentId];
if (parent) {
parent.children.push(nodeMap[item.id]);
}
}
});
// 提取根節(jié)點(diǎn)
return Object.values(nodeMap).filter(node => node.parentId === null);
}
const tree = buildTree(data);
console.log(JSON.stringify(tree, null, 2));解釋
初始化節(jié)點(diǎn)映射:
nodeMap 用于存儲(chǔ)每個(gè)節(jié)點(diǎn)的引用,并初始化每個(gè)節(jié)點(diǎn)的 children 為空數(shù)組。
建立父子關(guān)系:
遍歷數(shù)據(jù),找到每個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn),并將當(dāng)前節(jié)點(diǎn)添加到父節(jié)點(diǎn)的 children 中。
提取根節(jié)點(diǎn):
通過過濾 nodeMap 中 parentId 為 null 的節(jié)點(diǎn),得到樹的根節(jié)點(diǎn)。
遞歸函數(shù)實(shí)現(xiàn)
function buildTree(flatData, parentId = null) {
return flatData
.filter(node => node.parentId === parentId)
.map(node => ({
...node,
children: buildTree(flatData, node.id)
}));
}參數(shù)說明
flatData: 扁平的數(shù)組數(shù)據(jù)。每個(gè)元素包含id和parentId,用于表示節(jié)點(diǎn)和它們的父節(jié)點(diǎn)關(guān)系。parentId: 當(dāng)前要處理的父節(jié)點(diǎn)的 ID。默認(rèn)為null,表示初始調(diào)用時(shí)的根節(jié)點(diǎn)。
函數(shù)實(shí)現(xiàn)細(xì)節(jié)
過濾節(jié)點(diǎn):
flatData.filter(node => node.parentId === parentId)
filter方法會(huì)返回flatData中所有parentId等于當(dāng)前parentId的節(jié)點(diǎn)。這意味著我們?cè)趯ふ宜兄苯幼庸?jié)點(diǎn)。- 初次調(diào)用時(shí),
parentId是null,所以我們得到的是所有根節(jié)點(diǎn)。
映射節(jié)點(diǎn):
.map(node => ({
...node,
children: buildTree(flatData, node.id)
}))map方法對(duì)過濾后的每個(gè)節(jié)點(diǎn)執(zhí)行回調(diào)函數(shù)。- 回調(diào)函數(shù)的目的是將當(dāng)前節(jié)點(diǎn)的
children屬性設(shè)置為一個(gè)遞歸調(diào)用buildTree函數(shù)的結(jié)果。
遞歸調(diào)用:
buildTree(flatData, node.id)
- 對(duì)于每個(gè)節(jié)點(diǎn),我們?cè)俅握{(diào)用
buildTree函數(shù),將當(dāng)前節(jié)點(diǎn)的id作為新的parentId。 - 這會(huì)找到當(dāng)前節(jié)點(diǎn)的所有子節(jié)點(diǎn),并將這些子節(jié)點(diǎn)作為當(dāng)前節(jié)點(diǎn)的
children屬性。
遞歸過程
初始調(diào)用:
buildTree(flatData)
第一次調(diào)用時(shí) parentId 是 null,這意味著我們查找所有根節(jié)點(diǎn)。
查找子節(jié)點(diǎn):
對(duì)于每一個(gè)根節(jié)點(diǎn),buildTree 會(huì)被調(diào)用,查找該節(jié)點(diǎn)的直接子節(jié)點(diǎn)。
逐級(jí)遞歸:
對(duì)每個(gè)子節(jié)點(diǎn),buildTree 會(huì)遞歸調(diào)用自身,繼續(xù)查找該子節(jié)點(diǎn)的子節(jié)點(diǎn),直到所有節(jié)點(diǎn)的 children 屬性都被填充。
示例
假設(shè)我們有以下扁平數(shù)據(jù):
const data = [
{ id: 1, name: 'Root', parentId: null },
{ id: 2, name: 'Child 1', parentId: 1 },
{ id: 3, name: 'Child 2', parentId: 1 },
{ id: 4, name: 'Grandchild 1', parentId: 2 },
{ id: 5, name: 'Grandchild 2', parentId: 2 }
];調(diào)用 buildTree(data) 時(shí):
第一層:
parentId 是 null,找到 ID 為 1 的根節(jié)點(diǎn)。
第二層:
對(duì)于根節(jié)點(diǎn)(ID 為 1),調(diào)用 buildTree(data, 1) 查找其子節(jié)點(diǎn),找到 ID 為 2 和 3 的節(jié)點(diǎn)。
第三層:
對(duì)于 ID 為 2 的節(jié)點(diǎn),調(diào)用 buildTree(data, 2) 查找其子節(jié)點(diǎn),找到 ID 為 4 和 5 的節(jié)點(diǎn)。
第四層:
ID 為 4 和 5 的節(jié)點(diǎn)沒有子節(jié)點(diǎn),所以遞歸終止,返回空的 children 數(shù)組。
最終得到的樹形數(shù)據(jù)結(jié)構(gòu)如下:
[
{
id: 1,
name: 'Root',
children: [
{
id: 2,
name: 'Child 1',
children: [
{ id: 4, name: 'Grandchild 1', children: [] },
{ id: 5, name: 'Grandchild 2', children: [] }
]
},
{
id: 3,
name: 'Child 2',
children: []
}
]
}
]總結(jié)
這個(gè) buildTree 函數(shù)使用了遞歸的方式來構(gòu)建樹形數(shù)據(jù)結(jié)構(gòu)。通過過濾、映射和遞歸調(diào)用,它逐層構(gòu)建每個(gè)節(jié)點(diǎn)的子節(jié)點(diǎn),直到所有節(jié)點(diǎn)的 children 屬性都被正確填充。這種方法簡(jiǎn)潔且高效,適合處理層級(jí)數(shù)據(jù)。
到此這篇關(guān)于JavaScript 實(shí)現(xiàn)普通數(shù)組數(shù)據(jù)轉(zhuǎn)化為樹形數(shù)據(jù)結(jié)構(gòu)的文章就介紹到這了,更多相關(guān)js普通數(shù)組轉(zhuǎn)化樹形結(jié)構(gòu)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- JavaScript樹形結(jié)構(gòu)數(shù)組處理之遞歸問題
- JS前端二維數(shù)組生成樹形結(jié)構(gòu)示例詳解
- JavaScript數(shù)組扁平轉(zhuǎn)樹形結(jié)構(gòu)數(shù)據(jù)(Tree)的實(shí)現(xiàn)
- JS實(shí)現(xiàn)樹形結(jié)構(gòu)與數(shù)組結(jié)構(gòu)相互轉(zhuǎn)換并在樹形結(jié)構(gòu)中查找對(duì)象
- JavaScript平鋪數(shù)組轉(zhuǎn)樹形結(jié)構(gòu)的實(shí)現(xiàn)示例
- 如何將JavaScript將數(shù)組轉(zhuǎn)為樹形結(jié)構(gòu)
相關(guān)文章
基于 Immutable.js 實(shí)現(xiàn)撤銷重做功能的實(shí)例代碼
這篇文章主要介紹了基于 Immutable.js 實(shí)現(xiàn)撤銷重做功能及一些需要注意的地方,需要的朋友可以參考下2018-03-03
JavaScript利用Canvas實(shí)現(xiàn)粒子動(dòng)畫倒計(jì)時(shí)
粒子動(dòng)畫就是頁面上通過發(fā)射許多微小粒子來表示不規(guī)則模糊物體。本文將利用canvas實(shí)現(xiàn)酷炫的粒子動(dòng)畫倒計(jì)時(shí),感興趣的小伙伴可以嘗試一下2022-12-12
Javascript的異步函數(shù)和Promise對(duì)象你了解嗎
這篇文章主要為大家詳細(xì)介紹了Javascript異步函數(shù)和Promise對(duì)象,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03
中級(jí)前端工程師必須要掌握的27個(gè)JavaScript 技巧(干貨總結(jié))
這篇文章主要介紹了中級(jí)前端工程師必須要掌握的27個(gè)JavaScript 技巧(干貨總結(jié)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-09-09
談?wù)凧avaScript中super(props)的重要性
今天小編就為大家分享一篇關(guān)于談?wù)凧avaScript中super(props)的重要性,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-02-02
JavaScript實(shí)現(xiàn)的商品搶購(gòu)倒計(jì)時(shí)功能示例
這篇文章主要介紹了JavaScript實(shí)現(xiàn)的商品搶購(gòu)倒計(jì)時(shí)功能,可實(shí)現(xiàn)分秒級(jí)別的實(shí)時(shí)顯示倒計(jì)時(shí)效果,涉及js日期時(shí)間計(jì)算與頁面元素動(dòng)態(tài)操作相關(guān)技巧,需要的朋友可以參考下2017-04-04
(function(){})()的用法與優(yōu)點(diǎn)
(function(){})()的用法與優(yōu)點(diǎn)...2007-03-03
JS 進(jìn)度條效果實(shí)現(xiàn)代碼整理
進(jìn)度條效果實(shí)現(xiàn)代碼,有助于緩解頁面顯示慢的頁面,給用戶一個(gè)等待時(shí)間的效果2011-05-05
批量實(shí)現(xiàn)面向?qū)ο蟮膶?shí)例代碼
本文為大家詳細(xì)介紹下面向?qū)ο蟮睦^承以及如何實(shí)現(xiàn)批量實(shí)現(xiàn)面向?qū)ο?,感興趣的可以參考下哈,希望對(duì)大家有所幫助2013-07-07

