递归写法练习~如树菜单递归记录等...

324 阅读6分钟

问题描述

对于树结构的数据的加工,一般有以下几种情况

  • 树结构--->树结构

    • 树节点增加key和value
    • 树节点删除key和value
    • 树节点key和value颠倒变换
  • 树结构--->扁平化数组

    • 树结构拍平
  • 扁平化数组--->树结构

    • 如sql查出来的数据是扁平化的数据,注意pid和pids字段
    • 后端把查出来的数据根据pids字段转成树结构(或者直接扔给前端前端来转树结构)

1. 树菜单更改其中的key、value

代码

	<script>
		let oldTree = [ // 后端返回很多数据,前端只使用其中一部分
			{
				name: '北京',
				level: 2,
			},
			{
				name: '上海',
				level: 2,
				children: [
					{
						name: '浦东新区',
						level: 3,
					},
					{
						name: '青浦区',
						level: 3,
					},
				]
			},
			{
				name: '广州',
				level: 2,
				children: [
					{
						name: '白云区',
						level: 3,
					}
				]
			},
		]

		let newTree = [] // newTree存放加工好前端使用的数据
		function getTreeData(oldTree, newTree) {
			oldTree.forEach((item) => {
				let newTreeObj = {
					label: item.name,
					hierarchy: item.level
				}
				if (item.children) { // c.children.length>0
					getTreeData(item.children, newTreeObj.children = [])
				}
				newTree.push(newTreeObj)
			});
		}
		getTreeData(oldTree, newTree)
		console.log(newTree)

	</script>

效果图

image.png

2. 给树结构加上层级level字段

代码

let tree = 
[
        {
                name: '中国',
                children: [
                        {
                                name: '北京',
                                children: [
                                        {
                                                name: '朝阳区'
                                        }
                                ]
                        },
                        {
                                name: "上海",
                                children: [
                                        {
                                                name: '浦东新区'
                                        }
                                ]
                        }
                ]
        }
]
function setTreeLevel(tree, level) {
        level = level + 1 // 注意这里的层级字段增加需要在循环外边加
        tree.forEach((item) => {
                item.level = level
                if (item.children) {
                        setTreeLevel(item.children, level)
                }
        });
        return tree
}
let res = setTreeLevel(tree, 0)
console.log(res);

效果图

image.png

3. 递归方法将扁平化数组转树菜单树结构

数据库中查询的数据是一行行的,也就是一个数据,不存在children子节点相关,需要前端自己加工...

4. 使用递归将多层(维)数组拍平

多维数组拍平的思路:

  • 遍历这个数组,得到数组中的每一项(有的项是普通的值,有的项仍旧是数组)
  • 再定义一个数组newArr,用于存储拍平后的数组值
  • 如果是普通的项,就直接将这一项追加pushnewArr内即可
  • 如果不是普通的项是数组(非数组就是普通的项)就递归遍历

写法一:单层函数

let arr = [1, [2, 3], [4, [5, [6]], 7]] // 定义一个多维数组

function isArr(params) { // 定义一个函数,用于判断是否是数组
    return Object.prototype.toString.call(params) === "[object Array]"
}

function flatFn(arr, newArr = []) { // 递归函数拍平多维数组
    arr.forEach((item) => {
        if (!isArr(item)) { // 若这一项不是数组就追加进去。
            newArr.push(item)
        } else { // 若这一项是数组就递归继续追加
            flatFn(item, newArr)
        }
    });
    return newArr
}

let res = flatFn(arr)
console.log('多维数组拍平', res);

写法二:双层函数

let arr = [1, [2, 3], [4, [5, [6]], 7]] // 定义一个多维数组

function flatFn(arr) {
    let newArr = []
    function digui(arr) { // 专门定义递归函数用来递归拍平
        arr.forEach((item) => {
            if (Array.isArray(item)) { // 若是数组,继续递归拍平
                digui(item)
            } else { // 若不是数组直接追加即可
                newArr.push(item)
            }
        });
    }
    digui(arr)
    return newArr
}

let res = flatFn(arr)
console.log('递归拍平函数', res);

打印的结果

[1, 2, 3, 4, 5, 6, 7] // 这样就把数组拍平了

reduce方式的递归

let arrData = [1, [2, [3, 4, 5]]];
function flatFn(params, tempArr = []) {
    let result = params.reduce((tempArr, item) => { // reduce函数需要一个临时变量,这里定义的临时变量是空数组
            if (!Array.isArray(item)) { // 如果遍历的这一项不是数组,只是普通的项,直接push丢进去进去即可
                    tempArr.push(item)
                    return tempArr // reduce函数需要将临时变量return出去
            } else {
                    // 如果是数组,就递归一下,flatFn接收两个参数,用于遍历的数组,以及用于存储进行拍平项的数组
                    return flatFn(item, tempArr)
            }
    }, tempArr)
    // 经过上面一波操作完毕,得到拍平的结果
    return result
}
let res = flatFn(arrData)
console.log('拍平', res);

判断是否是数组(知识点复习)

console.log(
    "typeof", typeof [1, 2, 3] // object
);
console.log(
    "constructor", [1,2,3].constructor === Array // true
);
console.log(
    "instanceof", [1, 2, 3] instanceof Array // true
);
console.log(
    "Object.prototype.toString.call(xxx)", Object.prototype.toString.call([1, 2, 3]) // [object Array]
);
console.log(
    "Array.isArray(xxx)", Array.isArray([1, 2, 3]) // true
);

优先推荐Object.prototype.toString.call()Array.isArray()

判断是否是对象(知识点复习)

let obj = { judge: 'Object' }
console.log(
    'typeof', typeof obj // object
);
console.log(
    'constructor', obj.constructor === Object // true
);
console.log(
    'instanceof', obj instanceof Object // true
);
console.log(
    'Object.prototype.toString.call', Object.prototype.toString.call(obj) // [object Object]
);

优先推荐Object.prototype.toString.call()instanceof

5. 递归手写一个深拷贝

思路是:

  • 判断要拷贝的数据是什么数据类型,是对象?是数组?或者是普通的数据
  • 然后定义一个变量用来存储深拷贝的结果。
  • 若是对象就forin这个对象,赋值的时候要递归赋值;
  • 若是数组for这个数组,数组追加push的时候要递归追加
let obj = {
    name: '孙悟空',
    age: 500,
    like: {
        eat: 'peach',
        change: '72变'
    },
    partner: ['唐僧', '猪八戒', '沙和尚', '白龙马']
}

function deepClone(params) {
    let result; // 1. 先定义一个变量(暂不确定这个变量的数据类型),最终是要将传进来的数据都拷贝在这个变量内
    if (Object.prototype.toString.call(params) === "[object Object]") { // 2. 若传进来的数据类型是对象
        result = {} // 3. 是对象的话,就把变量置为对象数据类型
        for (const key in params) { // 4. 同时遍历传进来的这个对象
            // result[key] = params[key] // 4.1 正常情况下直接赋值即可
            result[key] = deepClone(params[key]) // 5. 但是考虑到直接赋值的值可能是对象(数组),所以需要递归一下
        }
    } else if (Array.isArray(params)) { // 6. 若传进来的数据类型是数组
        result = [] // 7. 就把变量置为数组类型
        for (let i = 0; i < params.length; i++) { // 8. 同时遍历传进来的这个数组
            // result.push(params[i]) // 8.1 正常情况下直接push装一份数据即可
            result.push(deepClone(params[i])) // 9. 但是考虑到装的这一项是数组或是(数组)对象,所以需要递归一下
        }
    } else {
        result = params // 10. 最终会找到最里层的数据直接赋值
    }
    return result // 11. 最后将递归深拷贝的结果丢出去以供使用
}

let res = deepClone(obj)
console.log('深拷贝结果-->', res);

6. 递归删除对象中空属性(如post请求中的body对象参数)

工作中,给后端传参时,post请求,请求体传递一个对象,对象中如果有值是空的话,就不传递给后端了。所以在传参之前,要加工一下参数,如下代码:

let obj = {
    aaa: '你好',
    bbb: '',
    ccc: null,
    ddd: undefined,
    eee: {
        fff: '147',
        ggg: [],
        hhh: [1, 2, 3]
    }
}

function deleteEmptyKey(obj) {
    for (const key in obj) {
        // 对象处理
        if (Object.prototype.toString.call(obj[key]) === '[object Object]') {
            if (JSON.stringify(obj[key]) === '{}') { // 空对象直接删除即可
                delete obj[key]
            } else { // 非空对象,递归继续操作
                deleteEmptyKey(obj[key])
            }
        }
        // 数组处理
        else if (Array.isArray(obj[key])) {
            if (obj[key].length == 0) { // 空数组也直接删除
                delete obj[key]
            } else {
                // 非空数组就保留着,准备传递给后端
            }
        } else { // 普通数据类型处理
            if (obj[key] === '' | obj[key] === null | obj[key] === undefined) { // 空值啥的不要,不传递给后端
                delete obj[key]
            }
        }
    }
    return obj // 直接使用或者使用返回值均可
}
let res = deleteEmptyKey(obj)
console.log('删除对象中的空值', res);

7. 递归拼接完整路径

复制粘贴,运行直接看效果

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        let tree = [
            {
                name: '中国',
                path: '/china',
                children: [
                    {
                        name: '北京',
                        path: '/beijing',
                        children: [
                            {
                                name: '朝阳',
                                path: '/chaoyang',
                            },
                            {
                                name: '海淀',
                                path: '/haidian',
                            },
                        ]
                    },
                    {
                        name: '上海',
                        path: '/shanghai',
                        children: [
                            {
                                name: '浦东',
                                path: '/pudong',
                            },
                            {
                                name: '静安',
                                path: '/jingan',
                            },
                        ]
                    },
                ]
            },
            {
                name: '美国',
                path: '/american',
                children: [
                    {
                        name: '纽约',
                        path: '/newYork',
                        children: [
                            {
                                name: '中城',
                                path: '/middleCity',
                            },
                            {
                                name: '下城',
                                path: '/downCity',
                            },
                        ]
                    },
                    {
                        name: '洛杉矶',
                        path: '/losAngeles',
                        children: [
                            {
                                name: '长滩',
                                path: '/longBeach',
                            },
                            {
                                name: '圣安娜',
                                path: '/santaAna',
                            },
                        ]
                    },
                ]
            }
        ]

        function addFullPath(oldTree, pre) {
            oldTree.forEach((item) => {
                item['fullPath'] = pre + '' + item.path
                if (item.children) {
                    addFullPath(item.children, item.fullPath)
                }
            });
            return oldTree
        }

        const result = addFullPath(tree, '')
        console.log('加完整路径结果-->', result);

        // 示例结果代码:

        let r = [
            {
                "name": "中国",
                "path": "/china",
                "children": [
                    {
                        "name": "北京",
                        "path": "/beijing",
                        "children": [
                            {
                                "name": "朝阳",
                                "path": "/chaoyang",
                                "fullPath": "/china/beijing/chaoyang"
                            },
                            {
                                "name": "海淀",
                                "path": "/haidian",
                                "fullPath": "/china/beijing/haidian"
                            }
                        ],
                        "fullPath": "/china/beijing"
                    },
                    {
                        "name": "上海",
                        "path": "/shanghai",
                        "children": [
                            {
                                "name": "浦东",
                                "path": "/pudong",
                                "fullPath": "/china/shanghai/pudong"
                            },
                            {
                                "name": "静安",
                                "path": "/jingan",
                                "fullPath": "/china/shanghai/jingan"
                            }
                        ],
                        "fullPath": "/china/shanghai"
                    }
                ],
                "fullPath": "/china"
            },
            {
                "name": "美国",
                "path": "/american",
                "children": [
                    {
                        "name": "纽约",
                        "path": "/newYork",
                        "children": [
                            {
                                "name": "中城",
                                "path": "/middleCity",
                                "fullPath": "/american/newYork/middleCity"
                            },
                            {
                                "name": "下城",
                                "path": "/downCity",
                                "fullPath": "/american/newYork/downCity"
                            }
                        ],
                        "fullPath": "/american/newYork"
                    },
                    {
                        "name": "洛杉矶",
                        "path": "/losAngeles",
                        "children": [
                            {
                                "name": "长滩",
                                "path": "/longBeach",
                                "fullPath": "/american/losAngeles/longBeach"
                            },
                            {
                                "name": "圣安娜",
                                "path": "/santaAna",
                                "fullPath": "/american/losAngeles/santaAna"
                            }
                        ],
                        "fullPath": "/american/losAngeles"
                    }
                ],
                "fullPath": "/american"
            }
        ]


    </script>
</body>

</html>