博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从一道春招笔试题说起 [上]
阅读量:6280 次
发布时间:2019-06-22

本文共 4235 字,大约阅读时间需要 14 分钟。

先来看这样一道题:

给定一个字典(对象),假设其中部分键值是有 '.' 号的字符串,试设计一个 nested 函数,使得其变成一个嵌套对象 (假设不存在重复键值)。

示例:

给定对象:

const obj = {  'A': 1,  'B.A': 2,  'B.B': 3,  'CC.D.E': 4,  'CC.D.F': 5}复制代码

应得到嵌套对象:

const nestedObj = {  'A': 1,  'B': {    'A': 2,    'B': 3  },  'CC': {    'D': {      'E': 4,      'F': 5    }  }}复制代码

题目其实很简单,考察的也就是基本的 JS 字符串和数组操作,我很快实现了这样一种代码:

// version 1.0const nested = obj => {  const res = {}  for (const key of Object.keys(obj)) {    if (key.indexOf('.') > -1) {      const [target, ...newKey] = key.split('.')      res[target] = nested({ [newKey.join('.')]: obj[key] }) // 递归处理剩余部分    } else res[key] = obj[key]  }  return res}复制代码
运行结果:
const obj = { 'A': 1, 'B.A': 2, 'B.B': 3, 'CC.D.E': 4, 'CC.D.F': 5 }console.log(nested(obj))// { A: 1, B: { B: 3 }, CC: { D: { F: 5 } } }复制代码

运行代码,很快发现了问题:这样直接赋值显然会覆盖掉已将存在的深层对象。这显然是不合理的,于是我使用 Object.assign 替代了原来赋值操作:

// version 1.1const nested = obj => {  const res = {}  for (const key of Object.keys(obj)) {    if (key.indexOf('.') > -1) {      const [target, ...newKey] = key.split('.')-      res[target] = nested({ [newKey.join('.')]: obj[key] }) // 递归处理剩余部分+      res[target] = Object.assign(+        res[target] ? res[target] : {},+        nested({ [newKey.join('.')]: obj[key] }) // 递归处理剩余部分+      )    } else res[key] = obj[key]  }  return res}复制代码

注意 Object.assign 不能向 Nil (也就是 nullundefined) 赋值,所以这里用三目运算符进行了包裹

运行结果:
const obj = { 'A': 1, 'B.A': 2, 'B.B': 3, 'CC.D.E': 4, 'CC.D.F': 5 }console.log(nested(obj))// { A: 1, B: { B: 3 }, CC: { D: { F: 5 } } }复制代码

运行代码,问题依然存在。

查阅 ,Object.assign 确实可以用于合并对象,而且浅层对象(≤2)的合并也都没有问题。

这使我想到了 lodash 提供的 方法,先拿来主义一下:

// version 1.2+ const { merge } = require('lodash')const nested = obj => {  const res = {}  for (const key of Object.keys(obj)) {    if (key.indexOf('.') > -1) {      const [target, ...newKey] = key.split('.')-      res[target] = Object.assign(+      res[target] = merge(+        res[target] ? res[target] : {},+        nested({ [newKey.join('.')]: obj[key] }) // 递归处理剩余部分+      )    } else res[key] = obj[key]  }  return res}复制代码
运行结果:
const obj = { 'A': 1, 'B.A': 2, 'B.B': 3, 'CC.D.E': 4, 'CC.D.F': 5 }console.log(nested(obj))// { A: 1, B: { A: 2, B: 3 }, CC: { D: { E: 4, F: 5 } } }复制代码

运行代码,成功!正确返回了预期的结果,可这是为什么呢?

继续查阅 ,浏览到 一节,这里是为了使不能原生支持 assign 的浏览器用上这个函数,大体上可以看作 assign 的源代码。可以看到,其实 assign 操作也只进行了一层遍历,并没有递归的处理类型为 objectvalue 值,使得深度 ≥ 1 的对象依然被覆盖;重新浏览文档,在一节也确实提到了对象的覆盖问题,例如:

const obj1 = { A: 1, B: { C: 2 } }const obj1 = { A: 2, B: { D: 3 } }console.log(Object.assign({}, obj1, obj2)) // ==> { A: 2, B: { D: 3 } }                                           // B.C 丢失了,因为前一个对象中的 { B: [Object] }                                            // 被后续的对象中的 { B: [Object] } 覆盖了复制代码

到 翻阅 lodash.merge 的源码,lodashmerge 操作是通过不断递归深拷贝来实现对象合并的,这样就不存在覆盖问题,例如:

const { merge } = require('lodash')const obj1 = { A: 1, B: { C: 2 } }const obj1 = { A: 2, B: { D: 3 } }console.log(merge({}, obj1, obj2)) // ==> { A: 2, B: { C: 2, D: 3 } }                                   // B.C 被正确合并了复制代码

基于这个思路,我简单实现了一个 merge 函数,修改原代码如下:

// version 2.0const baseMerge = (target, from) => {  const [newTarget, ...newFrom] = from  if (newFrom.length > 1) {    return baseMerge(target, [baseMerge(newTarget, newFrom)])  } else {    const keys = Object.keys(newTarget)    for (const key of keys) {      if (target.hasOwnProperty(key)) {        if (typeof target[key] === 'object') {          baseMerge(target[key], [newTarget[key]])        } else {          target[key] = newTarget[key]        }      } else target[key] = newTarget[key]    }    return target  }}const merge = (target, ...from) => baseMerge(target, Array.from(from))const nested = obj => {  const res = {}  for (const key of Object.keys(obj)) {    if (key.indexOf('.') > -1) {      const [target, ...newKey] = key.split('.')      res[target] = merge(        res[target] ? res[target] : {},        nested({ [newKey.join('.')]: obj[key] }) // 递归处理剩余部分      )    } else res[key] = obj[key]  }  return res}复制代码
运行结果:
const obj = { 'A': 1, 'B.A': 2, 'B.B': 3, 'CC.D.E': 4, 'CC.D.F': 5 }console.log(nested(obj))// { A: 1, B: { A: 2, B: 3 }, CC: { D: { E: 4, F: 5 } } } // 成功!复制代码

当然,这个 merge 函数与 lodash 实现的相比显然是不完善的,但根据题设,这里只存在对象和基本类型,所以这种简易实现应该也够用了。以上便是我对这道题的完整解题思路,如有任何问题或者好的建议,还请大家不吝指正。

参考链接

转载地址:http://oisva.baihongyu.com/

你可能感兴趣的文章
Delphi异形窗口之PNG
查看>>
mysql数据库优化(二)
查看>>
linux下部署.net 项目 参考网址
查看>>
采药 NOIP 2005 普及组
查看>>
自动化构建工具
查看>>
工作流模式每个工作流引擎都会支持多种方式的表单。目前大家讨论到的大概有三种。 动态表单 外置表单 普通表单...
查看>>
CDZSC_2015寒假新人(1)——基础 g
查看>>
Cloud9 on Docker镜像发送
查看>>
word文档字体显示不正常或没有想要的字体
查看>>
Apache Solr 访问权限控制
查看>>
常用数据结构[OpenCV 笔记12]
查看>>
Post Office Protocol --- pop协议
查看>>
点击向下展开的下拉菜单特效
查看>>
多版本office兼容办法
查看>>
[leetcode-566-Reshape the Matrix]
查看>>
discuz, 使用同一数据库, 只是换个环境, 数据就不一样了
查看>>
# 2017-2018-1 20155319 《信息安全系统设计基础》第14周学习总结
查看>>
UVA 816 Abbott's Revenge
查看>>
用python写算法5[二进制中1的个数]
查看>>
【新题】OCP 062题库出现很多新题-6
查看>>