昨天笔试遇到一个数组扁平化的问题,后来被提醒有一个很简便的方法,toString方法。
平时用到的地方可能只是在向后台传数组的时候,用的不是json格式需要特殊化处理,比如一些批量删除的功能,在以前工作的公司就是接受一个字符串,id通过逗号分隔,再传数组的时候用了toString。当然也有另一种传数组的方法,使用qs库调用里面提供的qs.stringify()
那回到数组扁平化的题
const a = [1,2,2,3,[4,6],[7,7,8,[9,10]],[5]]
将这个数组扁平化去重升序:
最简便的方法:
[...new Set(a.toString().split(',').map(item => +item))].sort((a,b) => a-b)
我当时的想法是遍历整个数组,如果当前元素是数组就递归,再把元素放进去,再判断是否有跟数组中重复,再要插入排序,这里也贴一下代码
var newArr = []
function parseArr(arr) {
arr.forEach(el => {
if(Array.isArray(el)) {
parseArr(el)
}else{
newArr.indexOf(el) === -1 && newArr.push(el)
for(let i=newArr.length - 2; i>=0 && newArr[i] > newArr[i+1];i--) {
let temp = newArr[i]
newArr[i] = newArr[i+1]
newArr[i+1] = temp
}
}
})
}
当数组较大的时候indexof去重那部分没有set快
还有被问到数组跟链表查找哪个比较快
一开始我以为那种应该会没有差别,但其实链表是通过指针去寻找下一个节点,并不是连续存储的,在移到下一个元素的时候数组会快一点
谈到优化资源,我讲到了可以在localStorge存储数据,但实际上用到更多的是sessionStorge,因为sessionStorge会在关闭页面就清除掉,在刷新页面的时候会被保留,而localStorge什么时候更新的问题,一开始想到的可以用时间戳,后来想到的是像webpack那样的chuckhash这种,如果文件改变了hash会改变,了解了一下这个hash怎么生成的,调用的是node.js的采用hash = crypto.createHash()方法指定一种创建hash的方法,常见有’sha256’, ‘sha512’,然后再调用hash.update(data),当然也有更简单的存一个版本号(对变动不频繁的这样也可)
对象扁平化这里是最常见的树形数组转成对象的那种,在之前公司采用的是通过利用id的唯一性构建一个对象来实现的,面试的时候要求是用递归,时间比较急没有写完,再谈题的时候提到可以提前退出递归,剪枝的地方可以考虑开一个visited数组如果visited过了就不再递归提前退出
var data = [
{ id: 1, parentId: 0, name: "一级菜单A", rank: 1 },
{ id: 2, parentId: 0, name: "一级菜单B", rank: 1 },
{ id: 3, parentId: 0, name: "一级菜单C", rank: 1 },
{ id: 4, parentId: 1, name: "二级菜单A-A", rank: 2 },
{ id: 5, parentId: 1, name: "二级菜单A-B", rank: 2 },
{ id: 6, parentId: 2, name: "二级菜单B-A", rank: 2 },
{ id: 7, parentId: 4, name: "三级菜单A-A-A", rank: 3 },
{ id: 8, parentId: 7, name: "四级菜单A-A-A-A", rank: 4 },
{ id: 9, parentId: 0, name: "五级菜单A-A-A-A-A", rank: 5 },
{ id: 10, parentId: 9, name: "六级菜单A-A-A-A-A-A", rank: 6 },
{ id: 11, parentId: 10, name: "七级菜单A-A-A-A-A-A-A", rank: 7 },
{ id: 12, parentId: 11, name: "八级菜单A-A-A-A-A-A-A-A", rank: 8 },
{ id: 13, parentId: 12, name: "九级菜单A-A-A-A-A-A-A-A-A", rank: 9 },
{ id: 14, parentId: 13, name: "十级菜单A-A-A-A-A-A-A-A-A-A", rank: 10 },
]
function parseTree(data) {
let newArr = []
let visited= new Array(data.length).fill(false)
function parseData(parent) {
for (let j = 0; j < data.length; j++) {
if(visited[j])continue
if (data[j].parentId === parent.id) {
visited[j] = true
newArr.push(data[j]);
(parent.children = parent.children || []).push(data[j])
parseData(data[j])
}
}
}
newArr.push({
id: 0,
children: []
})
parseData(newArr[0])
console.log(newArr)
}
parseTree(data)
个人比较喜欢object的方法,下面是我封装的一个方法,支持不同id和parentId的名字
export function parseTree(data, pid, idName, parentIdName) {
var newData = {}
data.forEach(function(item) {
var id = item[idName]
var _this = newData[id] = newData[id] ? Object.assign(item, newData[id]) : item
var pid = _this[parentIdName]
var parent = newData[pid] = newData[pid] || {};
(parent.children = parent.children || []).push(_this)
})
return newData[pid]
}
不需要递归,但是要有一个复制对象的动作
还有一点setTimeout(fn,delay),delay是可以省略的默认是0,就是较快执行,这个是我手写代码的时候忘记写延迟多少秒,被问如果这样写会不会报错,其实是不会报错的,而且还会在当主线程空闲的前提下执行
还有一点是闭包中传window对象的好处,除了想到变成局部变量,提高作用链寻找变量的这个点,就没有想到有什么其他好处了,希望知道的朋友可以告知一下