序文
ツリーをフラット化する、またはフラット化されたデータをツリー構造に変換するというニーズは、誰もがプロジェクトで遭遇したことがあると思います。これは通常、システム認証などのプロジェクトで発生します。次のステップは、データ処理をバックエンドで必要な形式に変換することです。
データの平坦化
- 平坦化された配列
let arr = [
{
id: 1, name: '1', pid: 0 },
{
id: 2, name: '2', pid: 1 },
{
id: 3, name: '3', pid: 1 },
{
id: 4, name: '4', pid: 3 },
{
id: 5, name: '5', pid: 3 },
]
- 木
let tree = [
{
id: 1,
name: '1',
pid: 0,
children: [
{
id: 2,
name: '2',
pid: 1,
children: [],
},
{
id: 3,
name: '3',
pid: 1,
children: [
{
id: 4,
name: '4',
pid: 3,
children: [],
},
],
},
],
},
]
木を平らにする
再帰的な実装
- ツリーを走査し、各項目を結果セットに追加します。子があり、長さが 0 でない場合は、再帰的に走査します。
- ここでは、構造化代入を使用して各ノードの子属性を削除する必要があります。
function treeToArray(tree) {
let res = []
for (const item of tree) {
const {
children, ...i } = item
if (children && children.length) {
res = res.concat(treeToArray(children))
}
res.push(i)
}
return res
}
実装を減らす
function treeToArray(tree) {
return tree.reduce((res, item) => {
const {
children, ...i } = item
return res.concat(
i,
children && children.length ? treeToArray(children) : []
)
}, [])
}
配列をツリーに平坦化する
再帰的な実装
- 最も一般的に使用される方法は再帰的実装であり、考え方は比較的単純です。ツリーの親ノードと親 ID を渡し、配列クエリをループし、対応する子ノードを見つけて親ノードにプッシュし、次に、子ノードを再帰的に検索します。
function arrayToTree(items) {
let res = []
let getChildren = (res, pid) => {
for (const i of items) {
if (i.pid === pid) {
const newItem = {
...i, children: [] }
res.push(newItem)
getChildren(newItem.children, newItem.id)
}
}
}
getChildren(res, 0)
return res
}
マップオブジェクトの実装
- 1. まずマップを転送し、対応する関係を見つけます。
- アイデア: まずデータをストレージ用のマップに変換し、次にオブジェクトの参照を使用して、トラバース中にストレージ用のマップから対応するデータを直接検索します。
- Object.prototype.hasOwnProperty: このメソッドは、オブジェクト自身のプロパティが指定されたプロパティを持つかどうかを示すブール値を返し、プロトタイプ チェーンから継承されたそれらのプロパティを無視します。
function arrayToTree(items) {
let res = [] // 存放结果集
let map = {
}
// 先转成map存储
for (const i of items) {
map[i.id] = {
...i, children: [] }
}
for (const i of items) {
const newItem = map[i.id]
if (i.pid === 0) {
res.push(newItem)
} else {
if (Object.prototype.hasOwnProperty.call(map, i.pid)) {
map[i.pid].children.push(newItem)
}
}
}
return res
}
- 2. マップの保存をしながら、対応関係を見つけます。
- アイデア: アイテムの ID をキーとしてループし、マップに保存します。キーと値のペアが既に存在する場合は、それを保存する必要はありません。同時に、アイテムの PID が一致していないかどうかを確認します。マップのキーに直接親子関係を対応付ける そうでない場合は、マップ内にキーと値のペアを生成するだけで、キーはpidとなり、親子関係に対応します
function arrayToTree(items) {
let res = [] // 存放结果集
let map = {
}
// 判断对象是否有某个属性
let getHasOwnProperty = (obj, property) =>
Object.prototype.hasOwnProperty.call(obj, property)
// 边做map存储,边找对应关系
for (const i of items) {
map[i.id] = {
...i,
children: getHasOwnProperty(map, i.id) ? map[i.id].children : [],
}
const newItem = map[i.id]
if (i.pid === 0) {
res.push(newItem)
} else {
if (!getHasOwnProperty(map, i.pid)) {
map[i.pid] = {
children: [],
}
}
map[i.pid].children.push(newItem)
}
}
return res
}