手撕数组扁平化 | 青训营

62 阅读3分钟

开门见山,什么是数组扁平化呢,就是把一个多维数组变为一维数组。如下就是一个5维数组。

不考虑兼容的情况下,ES6中提供了Array.prototype.flat处理.

flat(depth)  方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

let arr = [
    [1, 2, 2],
    [3, 4, 5, 5],
    [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10
]

/*ES6方法直接实现*/
// arr=arr.flat(Infinity); // 完全扁平化
// [
//   1,  2,  2, 3,  4,  5,  5,
//   6,  7,  8, 9, 11, 12, 12,
//  13, 14, 10
// ]

toSting方法,转化为字符串,再拼接为数组,需要处理字符串转数字类型。

let arr = [[1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14]]]], 10];

arr=arr.toString() // 1,2,2,3,4,5,5,6,7,8,9,11,12,12,13,14,10 

arr = arr.toString().split(",") // ['1',  '2',  '2',  '3','4', '5',  '5',  '6','7',  '8',  '9',  '11','12', '12', '13', '14','10']

arr = arr
  .toString()
  .split(",")
  .map((item) => parseFloat(item)); // [1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10]
console.log(arr);

转化为JSON字符串,需要处理掉[]

arr=JSON.stringify(arr).replace(/(\[|\])/g,'').split(',').map(item=>parseFloat(item));

创建一个空容器,循环数组中的每一项,如果当前项不是数组就存进去,是数组就递归。

while(arr.some(item => Array.isArray(item))) {
    arr = [].concat(...arr)
}
//  [1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10]

自己递归处理,首先初始化一个空数组 result 用于存储扁平化后的结果,同时创建一个 _this 变量来引用数组对象本身。

定义了一个递归函数 fn,用于遍历数组的每一项并将其添加到 result 中。如果遍历到的项是数组,则递归调用 fn 进一步处理内部嵌套数组。

最后,fn(_this) 将会从原始的数组 _this 开始进行扁平化操作。

(function () {
  function myFlat() {
    let result = [],
      _this = this;
    //=>循环ARR中的每一项,把不是数组的存储到新数组中
    let fn = (arr) => {
      for (let i = 0; i < arr.length; i++) {
        let item = arr[i];
        if (Array.isArray(item)) {
          fn(item);
          continue;
        }
        result.push(item);
      }
    };
    fn(_this);
    return result;
  }
  Array.prototype.myFlat = myFlat;
})();
arr = arr.myFlat();

console.log(arr); 

那么如何实现控制深度的扁平化呢?

接受一个额外的参数 depth,用于控制扁平化的深度。递归函数 fn 会检查当前递归的深度是否小于 depth,如果小于则继续递归展开子数组,否则直接将子数组的元素添加到结果中。

(function () {
  function myFlat(depth = 1) {
    let result = [],
      _this = this;

    let fn = (arr, currDepth) => {
      for (let i = 0; i < arr.length; i++) {
        let item = arr[i];
        if (Array.isArray(item) && currDepth < depth) {
          fn(item, currDepth + 1);
        } else {
          result.push(item);
        }
      }
    };

    fn(_this, 1);
    return result;
  }

  Array.prototype.myFlat = myFlat;
})();

arr = arr.myFlat(2);

[1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, Array(3), 10]

非递归方式实现扁平化,使用

  • 使用一个栈(stack 数组)来进行迭代。初始时,将原数组 _this 的元素都复制到栈中。
  • 进入循环,不断从栈中弹出元素。如果弹出的元素是数组,将其元素依次推入栈中,以便后续继续处理嵌套数组。
  • 如果弹出的元素不是数组,将其添加到结果数组 result 中。
  • 循环结束后,由于元素是从栈的尾部弹出的,为了保持原有的顺序,使用 reverse() 方法将结果数组进行翻转。
(function () {
  function myFlat() {
    let result = [],
      _this = this;
    let stack = [..._this];
    while (stack.length) {
      let item = stack.pop();
      if (Array.isArray(item)) {
        stack.push(...item);
      } else {
        result.push(item);
      }
    }
    return result.reverse();
  }

  Array.prototype.myFlat = myFlat;
})();
arr = arr.myFlat();

console.log(arr); // [1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14, 10]