前言
框架思維非常重要,和語言無關,這是一種非常重要的抽象能力,吹得厲害一點,就是要有高屋建瓴,統籌全局的能力。
無限級樹型結構的創建也是有套路的,下面由偉大的詩人chenqionghe給出套路框架,再分別套上相應的編程語言實現。
最終達到看到框架就能實現對應編程語言的代碼,或者也可以直接copy實現拿去用~
迭代實現參考了這篇文章的思路列表list轉樹形結構:(python/golang/js/php),童鞋們可以看看這篇文章
需求
給定一級列表,構造出指定父ID的無限級樹型結果
數據
[
{'id': 1, 'parent_id': 0, 'name': "A"},
{'id': 2, 'parent_id': 1, 'name': "AA"},
{'id': 3, 'parent_id': 1, 'name': "AB"},
{'id': 4, 'parent_id': 3, 'name': "ABA"},
{'id': 5, 'parent_id': 3, 'name': "ABB"},
{'id': 6, 'parent_id': 3, 'name': "ABC"},
{'id': 7, 'parent_id': 1, 'name': "AC"},
{'id': 8, 'parent_id': 7, 'name': "ACA"},
{'id': 9, 'parent_id': 8, 'name': "ACAA"},
{'id': 10, 'parent_id': 8, 'name': "ACAB"},
]
結果
最后構造成一棵根據父ID連接起來的樹結構
{
"id": 1,
"parent_id": 0,
"name": "A",
"children": [
{
"id": 2,
"parent_id": 1,
"name": "AA",
"children": []
},
{
"id": 3,
"parent_id": 1,
"name": "AB",
"children": [
{
"id": 4,
"parent_id": 3,
"name": "ABA",
"children": []
},
{
"id": 5,
"parent_id": 3,
"name": "ABB",
"children": []
},
{
"id": 6,
"parent_id": 3,
"name": "ABC",
"children": []
}
]
},
{
"id": 7,
"parent_id": 1,
"name": "AC",
"children": [
{
"id": 8,
"parent_id": 7,
"name": "ACA",
"children": [
{
"id": 9,
"parent_id": 8,
"name": "ACAA",
"children": []
},
{
"id": 10,
"parent_id": 8,
"name": "ACAB",
"children": []
}
]
}
]
}
]
}
框架
遞歸框架
遞歸是一門藝術,用接近人類的語言來表達了程序,優點是代碼較少,缺點是性能較差。
獲取樹(列表,父ID)
res = []
for 節點 in 列表:
if 節點的parent_id 等於 父ID
節點.children = 獲取樹(列表, 節點ID)
res.add(節點)
return res
迭代框架
迭代實現本質是創建一條引用鏈,將所有的節點串起來
獲取樹(列表,父ID)
memo = {}
for 節點 in 列表:
//構造memo給節點的父ID查找追加節點用
if 節點ID in memo:
節點.children = memo[節點ID].children //之前構造的children數組覆蓋當前節點的children
memo[節點ID] = 節點
else
節點.children = []
memo[節點ID] = 節點
//給像父對象的children追加
if 節點父ID in memo:
memo[節點父ID].children.add(memo[節點ID]) //追加當前構造的ID節點
else:
memo[節點父ID] = {'children':[memo[節點ID]]} //初始化父對象再追加
return memo[父ID].children
遞歸框架實現
python
def get_tree_iterative(list_data, parent_id=0):
memo = {}
for v in list_data:
item_id = v['id']
item_paren_id = v['parent_id']
if item_id in memo:
v['children'] = memo[item_id]['children']
memo[item_id] = v
else:
v['children'] = []
memo[item_id] = v
if item_paren_id in memo:
memo[item_paren_id]['children'].append(memo[item_id])
else:
memo[item_paren_id] = {'children': memo[item_id]}
return memo[parent_id]['children']
list_data = [
{'id': 1, 'parent_id': 0, 'name': "A"},
{'id': 2, 'parent_id': 1, 'name': "AA"},
{'id': 3, 'parent_id': 1, 'name': "AB"},
{'id': 4, 'parent_id': 3, 'name': "ABA"},
{'id': 5, 'parent_id': 3, 'name': "ABB"},
{'id': 6, 'parent_id': 3, 'name': "ABC"},
{'id': 7, 'parent_id': 1, 'name': "AC"},
{'id': 8, 'parent_id': 7, 'name': "ACA"},
{'id': 9, 'parent_id': 8, 'name': "ACAA"},
{'id': 10, 'parent_id': 8, 'name': "ACAB"},
]
res = get_tree_iterative(list_data)
import json
print(json.dumps(res, indent=4))
運行如下
golang
package main
import (
"encoding/json"
"fmt"
)
type Node struct {
Id int `json:"id"`
ParentId int `json:"parent_id"`
Name string `json:"name"`
Children []*Node `json:"children"`
}
func getTreeRecursive(list []*Node, parentId int) []*Node {
res := make([]*Node, 0)
for _, v := range list {
if v.ParentId == parentId {
v.Children = getTreeRecursive(list, v.Id)
res = append(res, v)
}
}
return res
}
func main() {
list := []*Node{
{4, 3, "ABA", nil},
{3, 1, "AB", nil},
{1, 0, "A", nil},
{2, 1, "AA", nil},
{5, 3, "ABB", nil},
{6, 3, "ABC", nil},
{7, 1, "AC", nil},
{8, 7, "ACA", nil},
{9, 8, "ACAA", nil},
{10, 8, "ACAB", nil},
}
res := getTreeRecursive(list, 0)
bytes, _ := json.MarshalIndent(res, "", " ")
fmt.Printf("%s\n", bytes)
}
運行如下
php
function getTreeRecursive(&$list, $parentId = 0)
{
$res = [];
foreach ($list as $k => $v) {
if ($v['parent_id'] == $parentId) {
$v['children'] = getTreeRecursive($list, $v['id']);
$res[] = $v;
}
}
return $res;
}
$list = [
['id' => 4, 'parent_id' => 3, 'name' => "ABA"],
['id' => 3, 'parent_id' => 1, 'name' => "AB"],
['id' => 1, 'parent_id' => 0, 'name' => "A"],
['id' => 2, 'parent_id' => 1, 'name' => "AA"],
['id' => 5, 'parent_id' => 3, 'name' => "ABB"],
['id' => 6, 'parent_id' => 3, 'name' => "ABC"],
['id' => 7, 'parent_id' => 1, 'name' => "AC"],
['id' => 8, 'parent_id' => 7, 'name' => "ACA"],
['id' => 9, 'parent_id' => 8, 'name' => "ACAA"],
['id' => 10, 'parent_id' => 8, 'name' => "ACAB"],
];
$res = getTreeRecursive($list);
echo json_encode($res, JSON_PRETTY_PRINT);
運行結果如下
js
function getTreeRecursive(listData, parentId = 0) {
let res = []
listData.forEach((v, k) => {
if (v.parent_id == parentId) {
v.children = getTreeRecursive(listData, v.id)
res.push(v)
}
})
return res
}
dataList = [
{'id': 1, 'parent_id': 0, 'name': "A"},
{'id': 2, 'parent_id': 1, 'name': "AA"},
{'id': 3, 'parent_id': 1, 'name': "AB"},
{'id': 4, 'parent_id': 3, 'name': "ABA"},
{'id': 5, 'parent_id': 3, 'name': "ABB"},
{'id': 6, 'parent_id': 3, 'name': "ABC"},
{'id': 7, 'parent_id': 1, 'name': "AC"},
{'id': 8, 'parent_id': 7, 'name': "ACA"},
{'id': 9, 'parent_id': 8, 'name': "ACAA"},
{'id': 10, 'parent_id': 8, 'name': "ACAB"},
]
let res = getTreeRecursive(dataList);
console.log((JSON.stringify(res, null, 4)))
運行如下
迭代框架實現
python
def get_tree_iterative(list_data, parent_id=0):
memo = {}
for v in list_data:
item_id = v['id']
item_paren_id = v['parent_id']
if item_id in memo:
v['children'] = memo[item_id]['children']
memo[item_id] = v
else:
v['children'] = []
memo[item_id] = v
if item_paren_id in memo:
memo[item_paren_id]['children'].append(memo[item_id])
else:
memo[item_paren_id] = {'children': [memo[item_id]]}
return memo[parent_id]['children']
list_data = [
{'id': 1, 'parent_id': 0, 'name': "A"},
{'id': 2, 'parent_id': 1, 'name': "AA"},
{'id': 3, 'parent_id': 1, 'name': "AB"},
{'id': 4, 'parent_id': 3, 'name': "ABA"},
{'id': 5, 'parent_id': 3, 'name': "ABB"},
{'id': 6, 'parent_id': 3, 'name': "ABC"},
{'id': 7, 'parent_id': 1, 'name': "AC"},
{'id': 8, 'parent_id': 7, 'name': "ACA"},
{'id': 9, 'parent_id': 8, 'name': "ACAA"},
{'id': 10, 'parent_id': 8, 'name': "ACAB"},
]
res = get_tree_iterative(list_data)
import json
print(json.dumps(res, indent=4))
運行如下
golang
package main
import (
"encoding/json"
"fmt"
)
type Node struct {
Id int `json:"id"`
ParentId int `json:"parent_id"`
Name string `json:"name"`
Children []*Node `json:"children"`
}
func getTreeIterative(list []*Node, parentId int) []*Node {
memo := make(map[int]*Node)
for _, v := range list {
if _, ok := memo[v.Id]; ok {
v.Children = memo[v.Id].Children
memo[v.Id] = v
} else {
v.Children = make([]*Node, 0)
memo[v.Id] = v
}
if _, ok := memo[v.ParentId]; ok {
memo[v.ParentId].Children = append(memo[v.ParentId].Children, memo[v.Id])
} else {
memo[v.ParentId] = &Node{Children: []*Node{memo[v.Id]}}
}
}
return memo[parentId].Children
}
func main() {
list := []*Node{
{4, 3, "ABA", nil},
{3, 1, "AB", nil},
{1, 0, "A", nil},
{2, 1, "AA", nil},
{5, 3, "ABB", nil},
{6, 3, "ABC", nil},
{7, 1, "AC", nil},
{8, 7, "ACA", nil},
{9, 8, "ACAA", nil},
{10, 8, "ACAB", nil},
}
res := getTreeIterative(list, 0)
bytes, _ := json.MarshalIndent(res, "", " ")
fmt.Printf("%s\n", bytes)
}
運行如下
php
function getTreeIterative($list, $parentId = 0)
{
$memo = [];
foreach ($list as &$v) {
$id = $v['id'];
$itemParentId = $v['parent_id'];
if (isset($memo[$id])) {
$v['children'] = &$memo[$id]['children'];
$memo[$id] = $v;
} else {
$v['children'] = [];
$memo[$id] = $v;
}
if (isset($memo[$itemParentId])) {
$memo[$itemParentId]['children'][] = &$memo[$id];
} else {
$memo[$itemParentId] = ['children' => [&$memo[$id]]];
}
}
return $memo[$parentId]['children'];
}
$list = [
['id' => 4, 'parent_id' => 3, 'name' => "ABA"],
['id' => 3, 'parent_id' => 1, 'name' => "AB"],
['id' => 1, 'parent_id' => 0, 'name' => "A"],
['id' => 2, 'parent_id' => 1, 'name' => "AA"],
['id' => 5, 'parent_id' => 3, 'name' => "ABB"],
['id' => 6, 'parent_id' => 3, 'name' => "ABC"],
['id' => 7, 'parent_id' => 1, 'name' => "AC"],
['id' => 8, 'parent_id' => 7, 'name' => "ACA"],
['id' => 9, 'parent_id' => 8, 'name' => "ACAA"],
['id' => 10, 'parent_id' => 8, 'name' => "ACAB"],
];
$res = getTreeIterative($list);
echo json_encode($res, JSON_PRETTY_PRINT);
運行結果如下
js
function getTreeIterative(listData, parentId = 0) {
let memo = {};
listData.forEach((v, k) => {
let id = v.id
let itemParentId = v.parent_id
if (memo[id]) {
v.children = memo[id].children
memo[id] = v
} else {
v.children = []
memo[id] = v;
}
if (memo[itemParentId]) {
memo[itemParentId].children.push(memo[id]);
} else {
memo[itemParentId] = {children: [memo[id]]};
}
})
return memo[parentId].children
}
dataList = [
{'id': 1, 'parent_id': 0, 'name': "A"},
{'id': 2, 'parent_id': 1, 'name': "AA"},
{'id': 3, 'parent_id': 1, 'name': "AB"},
{'id': 4, 'parent_id': 3, 'name': "ABA"},
{'id': 5, 'parent_id': 3, 'name': "ABB"},
{'id': 6, 'parent_id': 3, 'name': "ABC"},
{'id': 7, 'parent_id': 1, 'name': "AC"},
{'id': 8, 'parent_id': 7, 'name': "ACA"},
{'id': 9, 'parent_id': 8, 'name': "ACAA"},
{'id': 10, 'parent_id': 8, 'name': "ACAB"},
]
let res = getTreeIterative(dataList);
console.log((JSON.stringify(res, null, 4)))
運行如下
擴展
添加level
如果給節點添加一個處於的層級屬性怎么辦,其實用遞歸很簡單,只要增加一個參數就行了,下面給出python代碼
- 遞歸實現添加level
def get_tree_recursive(list_data, parent_id=0, level=0):
res = []
for v in list_data:
if v['parent_id'] == parent_id:
v['level'] = level
v['children'] = get_tree_recursive(list_data, v['id'], level + 1)
res.append(v)
return res
list_data = [
{'id': 1, 'parent_id': 0, 'name': "A"},
{'id': 2, 'parent_id': 1, 'name': "AA"},
{'id': 3, 'parent_id': 1, 'name': "AB"},
{'id': 4, 'parent_id': 3, 'name': "ABA"},
{'id': 5, 'parent_id': 3, 'name': "ABB"},
{'id': 6, 'parent_id': 3, 'name': "ABC"},
{'id': 7, 'parent_id': 1, 'name': "AC"},
{'id': 8, 'parent_id': 7, 'name': "ACA"},
{'id': 9, 'parent_id': 8, 'name': "ACAA"},
{'id': 10, 'parent_id': 8, 'name': "ACAB"},
]
res = get_tree_recursive(list_data)
import json
print(json.dumps(res, indent=4))
運行輸出
- 迭代實現添加level
這里就尷尬了,雖然是迭代,添加level還是得遞歸一下,這里加了一個add_tree_level方法
def get_tree_recursive(list_data, parent_id=0, level=0):
res = []
for v in list_data:
if v['parent_id'] == parent_id:
v['level'] = level
v['children'] = get_tree_recursive(list_data, v['id'], level + 1)
res.append(v)
return res
def get_tree_iterative(list_data, parent_id=0):
memo = {}
for v in list_data:
item_id = v['id']
item_paren_id = v['parent_id']
if item_id in memo:
v['children'] = memo[item_id]['children']
memo[item_id] = v
else:
v['children'] = []
memo[item_id] = v
if item_paren_id in memo:
memo[item_paren_id]['children'].append(memo[item_id])
else:
memo[item_paren_id] = {'children': [memo[item_id]]}
res = memo[parent_id]['children']
return add_tree_level(res)
def add_tree_level(children, level=0):
for v in children:
v['level'] = level
if len(v['children']) > 0:
add_tree_level(v['children'], level + 1)
return children
list_data = [
{'id': 1, 'parent_id': 0, 'name': "A"},
{'id': 2, 'parent_id': 1, 'name': "AA"},
{'id': 3, 'parent_id': 1, 'name': "AB"},
{'id': 4, 'parent_id': 3, 'name': "ABA"},
{'id': 5, 'parent_id': 3, 'name': "ABB"},
{'id': 6, 'parent_id': 3, 'name': "ABC"},
{'id': 7, 'parent_id': 1, 'name': "AC"},
{'id': 8, 'parent_id': 7, 'name': "ACA"},
{'id': 9, 'parent_id': 8, 'name': "ACAA"},
{'id': 10, 'parent_id': 8, 'name': "ACAB"},
]
res = get_tree_iterative(list_data)
import json
print(json.dumps(res, indent=4))
運行輸出
以后如果想用直接來拷貝代碼就行,就是這么簡單~