前端笔试常考:对后端返回的数据改造成级联数据!

写在前面

说起来级联数据改造,估计很多小伙伴都不陌生,这个问题在面试当中真的很容易问到,之前我好几个朋友在面试过程中就考察了这道笔试题。

因为这道面试题其实挺经典的,不管是后端生成路由表还是级联数据都会涉及到,从这道面试题中可以反映出很多问题,接下来我将举例并讲解这道题的解题思路,希望可以帮助到大家,不管是在面试还是工作当中都可以借助相同的思路去解决这类问题!

举例

我们先来看一下案例和要求吧!感兴趣的小伙伴不妨自己先来试试!这道题的解题思路有很多种,可以分享你觉得最好的解题思路哈,一起来探讨!

改造前:

js 复制代码
  let arr = [
    { id: 10, name: '部门5', pid: 8 },
    { id: 2, name: '部门2', pid: 1 },
    { id: 8, name: '部门4', pid: 3 },
    { id: 12, name: '部门1', pid: 11 },
    { id: 1, name: '部门1', pid: 0 },
    { id: 3, name: '部门3', pid: 1 },
    { id: 11, name: '部门1', pid: 0 },
  ]

改造后:

js 复制代码
[
    {
        "id": 11,
        "name": "部门1",
        "pid": 0,
        "children": [
            {
                "id": 12,
                "name": "部门1",
                "pid": 11,
                "children": []
            }
        ]
    },
    {
        "id": 1,
        "name": "部门1",
        "pid": 0,
        "children": [
            {
                "id": 2,
                "name": "部门2",
                "pid": 1,
                "children": []
            },
            {
                "id": 3,
                "name": "部门3",
                "pid": 1,
                "children": [
                    {
                        "id": 8,
                        "name": "部门4",
                        "pid": 3,
                        "children": [
                            {
                                "id": 10,
                                "name": "部门5",
                                "pid": 8,
                                "children": []
                            }
                        ]
                    }
                ]
            }
        ]
    }
]

解题

接下来我们开始解题,我会保证整个题解的时间复杂度为 O(n),如果 双层 for 循环嵌套将会 是O(n²),什么是时间复杂度?大家可以参考这篇文章。

(算法入门)人人都能看懂的时间复杂度和空间复杂度 - 掘金 (juejin.cn)

首先我们可以看到下面这个数组对象的数据

观察后得出 pid:0 为最顶层
id 是唯一的
pid 需要放在相对应的 id 下面
值得一提的是该数据是不规则的
子级可能在父级前面,父级可能在子级前面
所以我们需要在不知道顺序的情况下进行改造
所以接下来的思路既适用于有顺序还适用无顺序的情况

js 复制代码
  let arr = [
    { id: 10, name: '部门5', pid: 8 },
    { id: 2, name: '部门2', pid: 1 },
    { id: 8, name: '部门4', pid: 3 },
    { id: 12, name: '部门1', pid: 11 },
    { id: 1, name: '部门1', pid: 0 },
    { id: 3, name: '部门3', pid: 1 },
    { id: 11, name: '部门1', pid: 0 },
  ]

在这之前为了帮助大家更好的理解,我们先来一个小练习,请问下面的 a 最后是多少?

js 复制代码
  let a = { children: [] }
  let b = {
    children: [],
  }
  a.children.push(b)
  b.children.push({ d: '123' })

  console.log(a)

答案是

js 复制代码
{
    "children": [
        {
            "children": [
                {
                    "d": "123"
                }
            ]
        }
    ]
}

为什么呢?

原因是在复杂数据类型中,数组存储的是指针,也就是地址,会去指向当前元素的值,而复杂数据类型发生变化后,在其他用到的地方也会发生变化,这就是为什么有的时候我们需要深拷贝数据,就是不需要当前数据的变化影响到原数据,从而影响到其他用到该数据的地方。

好了,了解了上面的我们再来看这道题。

思路

这道题我们分为两部分,因为数据是没有顺序的,第一部分对数据进行改造,对原始数据添加 children,同时将数据顺序和 id 以键,值为索引的方式存放到 map 中,方便后面找到父级元素。

第一部分

我们将 id 设置为,因为 id 是唯一的,将设置为索引,后面通过 id 去找到元素的位置进行设置元素。

js 复制代码
    let hashMap = {},
      transfer = []

    for (let i = 0; i < itemList.length; i++) {
      hashMap[itemList[i].id] = i
      itemList[i].children = []
    }

hashMap 数据结构如下:

这样我们就可以根据 pid 找到对应的 id,也知道了其在数组中的位置,然后将其添加进去即可!

第二部分

分析一下这个代码,首先循环数组,判断是不是顶层元素(顶层元素的判断条也有其他的,大家需要根据具体条件去改,这里的是pid为0),如果是直接添加到 transfer 中!

如果不是,我们找到其父级元素在数组中的位置 ,然后添加到其 children 中即可。

js 复制代码
    for (let index = 0; index < itemList.length; index++) {
      if (itemList[index].pid !== 0 && hashMap[itemList[index].id] !== undefined) {
        itemList[hashMap[itemList[index].pid]].children.push(itemList[index])
      } else {
        transfer.push(itemList[index])
      }
    }

为什么如果不是顶级直接添加即可?

我们可以这样理解,这有几个元素 A,B,C,D,E

A是顶层元素,是 B 的父级,B 是 C 的父级,依次类推

如果我往 D 添加进去 E,C 添加 D,B 添加 C,A 添加 B,最终 A 是不是包含B,B 又包含 C,依次类推

虽然我们是一级一级的进行添加的,但是因为复杂数据类型的特性,我们在一级一级添加的同时,最终所有数据都会添加到 A 中,也就是我们上面写的 transfer 中,虽然我们只添加一次 transfer,但是因为层级关系,其实最后所有的数据都被添加到 transfer 中了。

总结

在级联数据改造中,还是离不开需要利用复杂数据类型的特性,今天的案例也充分的利用了这一点,我们首先对数据进行改造,将数据以 id=>index 形式存储起来,然后通过 pid 找到 id 对应的位置添加进去即可,很好理解。

怎么样,这种类型的题,如果以这个思路去解答是不是很不错呢?当然也有其他解题的思路,欢迎大家在评论区交流分享,互相学习!

完整代码

js 复制代码
  function transferData(itemList) {
    let hashMap = {},
      transfer = []

    for (let i = 0; i < itemList.length; i++) {
      hashMap[itemList[i].id] = i
      itemList[i].children = []
    }

    for (let index = 0; index < itemList.length; index++) {
      if (itemList[index].pid !== 0 && hashMap[itemList[index].id] !== undefined) {
        itemList[hashMap[itemList[index].pid]].children.push(itemList[index])
      } else {
        transfer.push(itemList[index])
      }
    }
    return transfer
  }
相关推荐
永乐春秋1 小时前
WEB攻防-通用漏洞&文件上传&js验证&mime&user.ini&语言特性
前端
鸽鸽程序猿1 小时前
【前端】CSS
前端·css
ggdpzhk1 小时前
VUE:基于MVVN的前端js框架
前端·javascript·vue.js
小曲曲2 小时前
接口上传视频和oss直传视频到阿里云组件
javascript·阿里云·音视频
学不会•3 小时前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
EasyNTS4 小时前
H.264/H.265播放器EasyPlayer.js视频流媒体播放器关于websocket1006的异常断连
javascript·h.265·h.264
活宝小娜5 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点5 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow5 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o5 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app