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