• 构造无限级树的框架套路,附上python/golang/php/js实现


    前言

    框架思维非常重要,和语言无关,这是一种非常重要的抽象能力,吹得厉害一点,就是要有高屋建瓴,统筹全局的能力。
    无限级树型结构的创建也是有套路的,下面由伟大的诗人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
    ", 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
    ", 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))
    
    

    运行输出

    以后如果想用直接来拷贝代码就行,就是这么简单~

  • 相关阅读:
    编译安装linux内核步骤
    怎样在github上协同开发
    在JSP里使用CKEditor和CKFinder
    tomcat结合nginx使用小结
    JAVA学习笔记——(五)
    JAVA学习笔记——(四)
    JAVA学习笔记——(三)
    JAVA学习笔记——(二)
    JAVA学习笔记——(一)
    ansible
  • 原文地址:https://www.cnblogs.com/chenqionghe/p/14868863.html
Copyright © 2020-2023  润新知