[算法]一步一步反扁平化对象

2,124 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

上一篇文章讲解了如何实现下列对象扁平化,这篇将讲解如何反扁平化(如何将newobj转化为obj)。

const obj = {
  a: 'a',
  b: [1,2,{c:[1,2,3,4]},4],
  c: { e: 5, f: 6 },
  d: undefined,
  f:[1,[1,2,[5,6]]]
};
//转换得到如下新数据结构:
const newobj = {
  a: 'a',
  'b[0]': 1,
  'b[1]': 2,
  'b[2].c[0]': 1,
  'b[2].c[1]': 2,
  'b[2].c[2]': 3,
  'b[2].c[3]': 4,
  'b[3]': 4,
  'c.e': 5,
  'c.f': 6,
  d: undefined,
  'f[0]': 1,
  'f[1][0]': 1,
  'f[1][1]': 2,
  'f[1][2][0]': 5,
  'f[1][2][1]': 6
}

1、数组转对象

  • 首先还是观察我们要转换的对象,它是一个只有一层的对象,我们要做的就是它的将key 值转化为对象结构,比如:c.e转换成c:{e:...},解决了这个问题那么这个功能就实现了50%。

  • 那么如何将c.e转化成对象结构呢?我们先实现一个如何将数组转化成对象:

// 将数组转换成链表结构(ps:链表在js中的表现其实就是一个对象,这里我们不转化链表,直接将数组转化成对象)
const arr  = ['a','b','c','d']
// 构造一个转换方法
function transformToLink(arr){
 // 构造一个我们需要的目标对象
 let head = {}
 // 构造一个指针,用来遍历数组切换
 let tmp = head

 for(let i =0;i<arr.length;i++) {
  let cur = arr[i]
  //当前数组值得下一个
  let next = arr[i+1]
  // 当下一个存在的时候,我们就构造一个新的对象赋值,否则我们直接赋值一个undefined
  if(next){
    let o = {}
    tmp[cur] = o
    //这里需要移动指针指向,将指针指向下一个对象
    tmp = o
  }else{
   // 没有我们就直接赋值
   tmp[cur] = undefined
  }
 }
return head
}

  • 测试一下,没问题:

image.png

  • 这步我们实现了数组转对象,那么c.e 不是就可以先split 分割成数组再转化嘛?

2、上一步我们实现了a.b.c.d.e.f转换成对象,但是还有'b[2].c[0]''f[1][2][0]'这类结构的key,接下来我们实现这部分的转化。

  • 首先还是先观察数据结构,当key中包含[]时候说明是个数组对象,我们再构造的时候就不能像上面一下构造对象而是构造数组,下面,我们将上面的方法修改一下:
//转化key eg:key = b[2][0].c[0]
function transformkey(key){
 const arr = key.split('.')
 let head = {}
 let tmp = head
 
 for(let i =0;i<arr.length;i++) {
  let key = arr[i]
  let nextKey = arr[i + 1];
  //这里需要判断key 是否包含[]字符串,如果是则是数组结构
  if(/\[.+?\]/g.test(key)){
   //可能是多维数组,匹配数组维度
    let indexs = key.match(/\[(.+?)\]/g);
    //获取数组的key值
    let _key = key.match(/^(.+?)\[/)[1];
    //构造一个新数组
    let n = []
    tmp[_key] = n
    //构造完数组对数组里面进行构造
    for(let j=0;j<indexs.length;j++){
      let index = indexs[j].replace(/\[|\]/g, '');
      let nextIndex = indexs[j+1]
      
      //数组包含数组
      if(nextIndex){
        let o = []
        n[index] = o
        //如果还包含数组,将n指针指向下一个数组
        n = o
      }else{
       //如果后面还有则构造对象
       if (nextKey) {
         let o = {}
         n[index] = o
         tmp = o
        } else {
         n[index] = undefined
        }
      }
    }
  } else {
     //不是数组和之前方法保持一致
     if (nextKey) {
        let o = {}
        tmp[key] = o
        tmp = o
     } else {
        tmp[key] = undefined
     }
  }

 }
 return head
}
  • 测试一下:

image.png

  • 数据是转化成功的,既然key-value 转换成功,那下面我们对整个对象进行转换,完成代码入下(注意转换是考虑构造新数组或新对象时,需要判断前面是否已经构造过,过这部分我会在代码中表示出来):
function unflatten(obj) {
  let o = {}
  for (let key in obj) {
    transformKey(key, obj[key], o)
  }
  return o
}
//转化key
function transformKey(key,value,head){
 const arr = key.split('.')
 let tmp = head
 for(let i =0;i<arr.length;i++) {
  let key = arr[i]
  let nextKey = arr[i + 1];
  //这里需要判断key 是否包含[]字符串,如果是则是数组结构
  if(/\[.+?\]/g.test(key)){
   //可能是多维数组,匹配数组维度
    let indexs = key.match(/\[(.+?)\]/g);
    //获取数组的key值
    let _key = key.match(/^(.+?)\[/)[1];
    //构造数组需要判断是否已经存在
    tmp[_key] = tmp[_key]?tmp[_key]:[]
    let n = tmp[_key]
     
    //构造完数组对数组里面进行构造
    for(let j=0;j<indexs.length;j++){
      let index = indexs[j].replace(/\[|\]/g, '');
      let nextIndex = indexs[j+1]
      
      //数组包含数组
      if(nextIndex){
       //构造数组需要判断是否已经存在
       
        n[index] = n[index]?n[index]:[]
        //如果还包含数组,将n指针指向下一个数组
        n = n[index]
        
      }else{
       //如果后面还有则构造对象
       if (nextKey) {
        //构造对象需要判断是否已经存在
         n[index] = n[index]?n[index]:{}
         tmp = n[index]
        } else {
         n[index] = value
        }
      }
    }
  } else {
     //不是数组和之前方法保持一致
     if (nextKey) {
       //构造对象需要判断是否已经存在
        tmp[key] = tmp[key]?tmp[key]:{}
        tmp = tmp[key] 
     } else {
        tmp[key] = value
     }
  }

 }
 return head
}
  • 测试结果:

image.png