Daily-Question icon indicating copy to clipboard operation
Daily-Question copied to clipboard

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

Open shfshanyue opened this issue 3 years ago • 11 comments

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)

shfshanyue avatar Dec 18 '20 03:12 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 直接实现!

shfshanyue avatar Dec 30 '20 16:12 shfshanyue

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

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

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

reveriesMeng avatar Jan 12 '21 02:01 reveriesMeng

@reveriesMeng 好的

shfshanyue avatar Jan 14 '21 15:01 shfshanyue

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

demon-zhonglin avatar Mar 24 '21 04:03 demon-zhonglin

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;
}

haotie1990 avatar Jul 26 '21 08:07 haotie1990

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

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

shfshanyue avatar Jul 31 '21 04:07 shfshanyue

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

illumi520 avatar Oct 28 '21 10:10 illumi520

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

illumi520 avatar Oct 28 '21 10:10 illumi520

一种使用迭代器的实现:

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
}

3fuyang avatar Mar 22 '22 15:03 3fuyang

支持depth为0

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

Vi-jay avatar May 12 '22 04:05 Vi-jay

基于递归实现,不用 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);
};

bldf avatar Jul 11 '22 08:07 bldf