前端面试手撕 对象的扁平化与反扁平化

2,613 阅读2分钟
首先说明对象的扁平化

输入输出如下

const source = { a: { b: { c: 1, d: 2 }, e: 3 }, f: { g: 2 } }
console.log(objectFlat(source));
const obj = {
  a: 1,
  b: [1, 2, { c: true }],
  c: { e: 2, f: 3 },
  g: null,
};
console.log(objectFlat(obj));


--------结果
{ 'a.b.c': 1, 'a.b.d': 2, 'a.e': 3, 'f.g': 2 }
{
  a: 1,
  'b[0]': 1,
  'b[1]': 2,
  'b[2].c': true,
  'c.e': 2,
  'c.f': 3,
  g: null
}

扁平化

我们从结果入手,可以知道我们需要对象进行遍历,把里面的属性值依次输出,所以我们可以知道核心方法体就是:传入对象的 key 值和 value,对 value 再进行递归遍历。

我们知道 js 的数据类型可以基础数据类型和引用数据类型,对于题目而言,基础数据类型无需再进行深层次遍历,引用数据类型需要再次进行递归。

function objectFlat(obj = ''){
  const res = {}
  function flat(item , preKey = ''){
    Object.entries(item).forEach(([key,value]) => {
      let newKey = key
      // console.log(value,typeof value,Array.isArray(value))
      if (Array.isArray(item)){
        // console.log('是数组')
        newKey = preKey ? `${preKey}[${key}]` : key
      }else{
        newKey = preKey ? `${preKey}.${key}` : key
      }
      if (value && typeof value === 'object'){
        flat(value , newKey)
      }else{
        res[newKey] = value
      }
    })
  }
  flat(obj)
  return res
}

const source = { a: { b: { c: 1, d: 2 }, e: 3 }, f: { g: 2 } }
console.log(objectFlat(source));
const obj = {
  a: 1,
  b: [1, 2, { c: true }],
  c: { e: 2, f: 3 },
  g: null,
};
console.log(objectFlat(obj));

进阶 反扁平化

有点草率,本部分不是完全的对象扁平化。而是扁平化解析取值 不想coding了(狗头标题党)

直接上代码


function testPropTypes(value, type, dev) {
  const nEnums = [
    '[object Number]',
    '[object String]',
    '[object Boolean]',
    '[object Undefined]',
    '[object Function]',
    '[object Null]',
    '[object Object]',
    '[object Array]',
    '[object Date]',
    '[object RegExp]',
    '[object Error]',
  ];
  const reg = new RegExp('\\[object (.*?)\\]');

  // 完全匹配模式,type应该传递类似格式[object Window] [object HTMLDocument] ...
  if (reg.test(type)) {
    // 排除nEnums的12种
    if (~nEnums.indexOf(type)) {
      if (dev === true) {
        console.warn(value, 'The parameter type belongs to one of 12 types:number string boolean undefined Null Object Array Date RegExp function Error NaN');
      }
    }

    if (Object.prototype.toString.call(value) === type) {
      return true;
    }

    return false;
  }
}
function getValue(obj, key, defaultValue) {
  // 结果变量
  const defaultResult = defaultValue === undefined ? undefined : defaultValue;

  if (testPropTypes(obj, 'Object') === false && testPropTypes(obj, 'Array') === false) {
    return defaultResult;
  }

  // 结果变量,暂时指向obj持有的引用,后续将可能被不断的修改
  let result = obj;

  // 得到知道值
  try {
    // 解析属性层次序列
    const keyArr = key.split('.');
    // console.log(keyArr[0])
    var res = []
    for (let i = 0; i < keyArr.length; i++) {
      let k0 = keyArr[i]
      // console.log(k0.split('['))
      k0 = k0.split('[')
      for (let i = 0; i < k0.length; i++) {
        if (k0[i] !== '') {
          res.push(k0[i][0])
        }
      }
    }
    console.log(res)
    // 迭代obj对象属性
    for (let i = 0; i < res.length; i++) {
      // 如果第 i 层属性存在对应的值则迭代该属性值
      if (result[res[i]] !== undefined) {
        result = result[res[i]];

        // 如果不存在则返回未定义
      } else {
        return defaultResult;
      }
    }
  } catch (e) {
    return defaultResult;
  }

  // 返回获取的结果
  return result;
}

// 示例
var object = { a: [{ b: { c: 3 } }] }; // path: 'a[0].b.c'
var array = [{ a: { b: [1] } }]; // path: '[0].a.b[0]'r
// res = 'a'

// function getValue(target, valuePath, defaultValue) {}

console.log(getValue(object, "a[0].b.c", 0)); // 输出3
console.log(getValue(array, "[0].a.b[0]", 12)); // 输出 1
console.log(getValue(array, "[0].a.b[0].c", 12)); // 输出 12

捞薯条了