前言
- 常网IT戳我呀!
- 常网IT源码上线啦!
- 本篇录入吊打面试官专栏,希望能祝君拿下Offer一臂之力,各位看官感兴趣可移步🚶。
- 有人说面试造火箭,进去拧螺丝;其实个人觉得问的问题是项目中涉及的点 || 热门的技术栈都是很好的面试体验,不要是旁门左道冷门的知识,实际上并不会用到的。
- 接下来请看某司的面试题:给你行政区域的数据,根据传入的层级返回对应的数据结构。
你一生追求的东西,
其实一开始就在。
一开始我就拥有世界上最珍贵的爱。
这一年慢慢才明白,
一生所求无非是爱与自由,
只是你后知后觉而已,
兜兜转转追寻一圈后,
才发现那把钥匙早已握在手中。
人无法同时拥有青春和对于青春的感受,
有些东西要靠失去才明证明它的珍贵。
一、问题剖析
现在我们有一份行政区划的数据(这是全部层级都有的);
需要根据传入的层级,如(只展示省,或者只展示省市)
来显示对应的数据结构。
二、结构
行政区划数据如下:
javascript
[
{
"code": "44",
"name": "广东省",
"children": [
{
"code": "4401",
"name": "广州市",
"children": [
{
"code": "440103",
"name": "荔湾区",
"children": [
{
"code": "440103001",
"name": "沙面街道"
},
]
}
]
},
]
},
{
"code": "11",
"name": "北京市",
"children":[
...
]
},
]
然后传入层级。
如:只需要展示省市,后面的区、街道就不需要展示了。
要求我们在这份数据结构中处理。
三、简单粗暴
于是,我们简单粗暴,可以写出实习生都会写的代码:
javascript
let provincesCitiesDistricts_tmp = data // 行政区划数据
if (__config__.formatArea == 1) {
// 1:省
provincesCitiesDistricts_tmp.forEach((f) => {
f.children = null;
});
} else if (__config__.formatArea == 2) {
// 2:省市
provincesCitiesDistricts_tmp.forEach((f) => {
Array.isArray(f.children) &&
f.children.forEach((c) => {
// 把市,后面的children赋为null
c.children = null;
});
});
} else if (__config__.formatArea == 3) {
// 3:省市区
provincesCitiesDistricts_tmp.forEach((f) => {
Array.isArray(f.children) &&
f.children.forEach((c) => {
Array.isArray(c.children) &&
c.children.forEach((c1) => {
c1.children = null;
});
});
});
} else if (__config__.formatArea == 4) {
// 4:省市区街道
provincesCitiesDistricts_tmp.forEach((f) => {
Array.isArray(f.children) &&
f.children.forEach((c) => {
Array.isArray(c.children) &&
c.children.forEach((c1) => {
Array.isArray(c1.children) &&
c1.children.forEach((c2) => {
c2.children = null;
});
});
});
});
}
obj["options"] = provincesCitiesDistricts_tmp; // 这是最终的结果
面试官一看,这是摇摇头,说出你句经典的话,你回去等通知吧!
于是你,泪流满面的狂跑而去🚶。
于是你行者孙,第二天有备而来,并说道,这一次,我肯定能写出让你欣喜万分的来。
你左牵黄,右擎苍。
娓娓写来。
四:优解
我们停下来思考一下🧐,如果传入的是省市(控制到第二层),那只是将children循环到第二级的时候,将children赋空即可。
我们在回味一下之前写的这段代码。
发现都很类似的写法。
那么,上面这部分的代码可以用递归实现。
javascript
// 设置children
const setChildren = (list, num = 0, index = 1) => {
Array.isArray(list) &&
list.forEach((f) => {
// 只要这两个相等,则表示这一层的children需要赋空
if (num == index) {
f.children = null;
} else {
index++;
setChildren(f.children, num, index); // 递归调用
}
});
};
let provincesCitiesDistricts_tmp = data // 行政区划数据
if (__config__.formatArea == 1) {
provincesCitiesDistricts_tmp.forEach((f) => {
f.children = null;
});
} else if (__config__.formatArea == 2) {
provincesCitiesDistricts_tmp.forEach((f) => {
setChildren(f.children, 1);
});
} else if (__config__.formatArea == 3) {
provincesCitiesDistricts_tmp.forEach((f) => {
setChildren(f.children, 2);
});
} else if (__config__.formatArea == 4) {
provincesCitiesDistricts_tmp.forEach((f) => {
setChildren(f.children, 3);
});
} else if (__config__.formatArea == 5) {
provincesCitiesDistricts_tmp.forEach((f) => {
setChildren(f.children, 4);
});
}
obj["optionsString"] = provincesCitiesDistricts_tmp;
别急,一步一步来😁。
到这里,我们巧用递归优化了很多代码,我们看到if...else也是很恶心,接着优化代码:
javascript
const setChildren = (list, num = 0, index = 1) => {
Array.isArray(list) &&
list.forEach((f) => {
if (num == index) {
f.children = null;
} else {
index++;
setChildren(f.children, num, index);
}
});
};
let provincesCitiesDistricts_tmp = data // 行政区划数据
if (__config__.formatArea == 1) {
provincesCitiesDistricts_tmp.forEach((f) => {
f.children = null;
});
} else if (__config__.formatArea) {
provincesCitiesDistricts_tmp.forEach((f) => {
setChildren(f.children, __config__.formatArea - 1);
});
}
obj["optionsString"] = provincesCitiesDistricts_tmp;
根据__config__.formatArea
是多少层,控制行政区域到多少层显示。
五:深拷贝递归
🙋面试官:你小紫,对递归很熟?那你用递归实现一下深拷贝吧。
🙋🏻♂️这不是信手拈来的事情嘛!
javascript
/**
* @description 提问:
* @description 1. 为什么使用WeakMap?
* @description 因为WeakMap是弱引用,可以防止递归进入死循环
* @description 2. 为什么使用obj.constructor()创建空对象?
* @description 构造函数新建一个空的对象,而不是使用{}或者[],这样可以保持原形链的继承
* @description 3. 有必要加上obj.hasOwnProperty(key)判断
* @description 判断属性是否来自原型链上,因为for..in..也会遍历其原型链上的可枚举属性
*
* @description 深拷贝(递归拷贝)
*
* @param {Array} obj 目标数组
* @param {WeakMap} [cache=new WeakMap()]
* @return {*}
* @memberof ArrayTool
*/
deepCopy1(obj, cache = new WeakMap()) {
if (obj === null || typeof obj !== "object") return obj
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
if (cache.has(obj)) return cache.get(obj) // 如果出现循环引用,则返回缓存的对象,防止递归进入死循环(Node的循环引用也返回缓存对象)
let cloneObj = new obj.constructor() // 使用对象所属的构造函数创建一个新对象
cache.set(obj, cloneObj) // 缓存对象,用于循环引用的情况
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = this.deepCopy1(obj[key], cache) // 递归拷贝
}
}
return cloneObj
}
这可以直接用在自己项目中的哦~
面试官摸了摸胡子,顺便摸了摸我那八块腹肌,明天来报到。
后记
其实这道题在实际项目中,行政区域很常见,行政区域数据无非是接口按需返回。
或者是在数据不变的情况下,数据结构放在前段项目中,由前段管理。
如果有其他更好的方法也欢迎评论区见,这里提供的只是诸多方法之一。
最后,祝君能拿下满意的offer。
我是Dignity_呱,来交个朋友呀,有朋自远方来,不亦乐乎呀!深夜末班车
👍 如果对您有帮助,您的点赞是我前进的润滑剂。