高级前端手写代码【Q443】实现一个数组扁平化的函数 flatten

实现一个数组扁平化的函数 flatten

更多描述 flatten 模拟 Array.prototype.flat 实现,默认展开一层,可传递参数用以展开多层

// [1, 2, 3, 4, [5, 6]]
flatten([1, 2, 3, [4, [5, 6]]]);
 
// [1, 2, 3, 4, 5, 6]
flatten([1, 2, 3, [4, [5, 6]]], 2);

Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 451

Author 回答者: shfshanyue

在 ES2019 之前,可通过 reduce + concat 实现,由于 Array.prototype.concat 既可以连接数组又可以连接单项,十分巧妙

const flatten = (list) => list.reduce((a, b) => a.concat(b), []);

一个更简单的实现方式是 Array.prototype.concat... 运算符

const flatten = (list) => [].concat(...list);

如果要求深层数组打平,则如下实现

const flatten = (list) =>
  list.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []);

如果要求如同原生 API Array.prototype.flat 一致的 API,可传入可扁平的深度。代码可见 【Q443】实现一个数组扁平化的函数 flatten

function flatten(list, depth = 1) {
  if (depth === 0) return list;
  return list.reduce(
    (a, b) => a.concat(Array.isArray(b) ? flatten(b, depth - 1) : b),
    [],
  );
}

在 ES2019 之后,可通过 Array.prototype.flat 直接实现!

Author 回答者: reveriesMeng

您好作者,您的实现方式最多只能降维一层深度。

const flatten = (list) =>
  list.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []);

不仅如此,ES2019的flat还支持传入depth来降维指定的深度。

Author 回答者: shfshanyue

@reveriesMeng 好的

Author 回答者: demon-zhonglin

const flatten = (arr, d = 1) => d > 0 ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatten(val, d - 1) : val), []) : arr.slice()

Author 回答者: haotie1990

function flat(array, depth = Number.MAX_VALUE) {
  let result = [...array];
  while (result.some((i) => Array.isArray(i) && depth > 0)) {
    // 注意concat方法的参数valueN,即可是数值也可以是数组,当时数组时存在隐形的展开操作
    // concat方法不改变原数组,返回一个新数组
    result = [].concat(...result);
    depth--;
  }
  return result;
}

Author 回答者: shfshanyue

@haotie1990 原生 API 默认是 1,当然这个题目我也没有规定,不过 depth 用 MAX_SAFE_INTEGER 或者 Infinity 好一些?

而且该 API 也不涉及对数组自身的操作,应该无需 [...array]

Author 回答者: illumi520

function flatten(arr) {
return arr.reduce((result, item)=> { return result.concat(Array.isArray(item) ? flatten(item) : item); }, []); }

Author 回答者: illumi520

function flatten(arr) { return arr.toString().split(’,‘).map(function(item) { return Number(item); }) }

Author 回答者: 3fuyang

一种使用迭代器的实现:

const flatten = function(target, depth = 1) {
  const copy = [...target]
  for(let i = 0; i < depth; ++i){
    const iter = copy[Symbol.iterator]()
    let item = null
    for(item = iter.next(); !item.done; ){
      // 注意:迭代器并不与可迭代对象某个时刻的快照绑定,而仅仅是用游标来记录遍历可迭代对象的历程,
      // 如果可迭代对象在迭代期间被修改了,那么迭代器也会反映相应的变化
      if(Array.isArray(item.value)){
        const temp = [...item.value]
        let size = temp.length
        for(let j = 0; j < size; ++j){
          item = iter.next()
        }
        copy.splice(copy.indexOf(item.value), 1, ...temp)
      }else{
        item = iter.next()
      }
    }
    /* for(let item of copy){
      if(Array.isArray(item)){
        const temp = [...item]
        copy.splice(copy.indexOf(item), 1, ...temp)
      }
    } */
  }
  return copy
}

Author 回答者: Vi-jay

支持depth为0

function flatten(arr = [], depth = 1) {
  return arr.reduce(
    (acc, cur) =>
      acc.concat(
        Array.isArray(cur) && depth > 0 ? flatten(cur, --depth) : [cur],
      ),
    [],
  );
}

Author 回答者: bldf

基于递归实现,不用 Array.concat

Array.prototype.myFlat = function (this: any[], depth: number = 1) {
  const myFlat = (
    arr: any[],
    flatLength = 1,
    resultArray = [] as any[],
    forEachCount = 0,
  ) => {
    arr.forEach((d: any) => {
      if (
        Array.isArray(d) &&
        (flatLength === -1 || forEachCount < flatLength)
      ) {
        myFlat(d, flatLength, resultArray, forEachCount + 1);
      } else {
        resultArray.push(d);
      }
    });
 
    return resultArray;
  };
 
  return myFlat(this, depth);
};