拍平数组的几种方式

Arrayflatten

工具方法定义

  • 获取变量类型
const getType = (obj, type) =>
  Object.prototype.toString.call(obj).replace(/\[object (\w+)]/, '$1') === type
1
2
  • 判断是否为数组
const isArray = (arr) =>
  Array.isArray ? Array.isArray(arr) : getType(arr, 'Array')
1
2

# for...of

function flatten(arr) {
  let res = []
  for (let item of arr) {
    res = res.concat(isArray(item) ? flatten(item) : item)
  }
  return res
}
let arr = [1, [2, 3, [4, 5]]]
flatten(arr) // [1, 2, 3, 4, 5]
1
2
3
4
5
6
7
8
9

# 控制深度

  • 利用闭包,新增一个 depth 变量,递归函数每调用一次,就减 1
let flatten = (depth) => {
  let _flatten = (arr) => {
    if (--depth <= 0) return arr
    let res = []
    for (let item of arr) {
      res = res.concat(isArray(item) ? _flatten(item) : item)
    }
    return res
  }
  return _flatten
}

let arr = [1, [2, 3, [4, 5]]]
flatten(2)(arr) // [1, 2, 3, [4, 5]]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 或者不使用闭包,每次都传入这个 depth
let flatten = (arr, depth) => {
  if (--depth <= 0) return arr
  let res = []
  for (let item of arr) {
    res = res.concat(isArray(item) ? _flatten(item, depth) : item)
  }
  return res
}

let arr = [1, [2, 3, [4, 5]]]
flatten(arr, 2) // [1, 2, 3, [4, 5]]
1
2
3
4
5
6
7
8
9
10
11

# reduce

function flatten(arr) {
  // 不是数组或数组长度小于等于 1, 则直接返回
  if (!isArray(arr) || !arr.length) return arr
  return arr.reduce((pre, cur) => {
    // 注意concat和push两个方法对原数组的影响
    isArray(cur) ? (pre = pre.concat(flatten(cur))) : pre.push(cur)
    return pre
  }, [])
}
let arr = [1, [2, 3, [4, 5]]]
flatten(arr) // [1, 2, 3, 4, 5]
1
2
3
4
5
6
7
8
9
10
11

# 控制深度

let flatten = (depth) => {
  return function _flatten(arr) {
    // 不是数组或数组长度小于等于 1, 则直接返回
    if (--depth <= 0 || !isArray(arr) || arr.length <= 1) return arr
    return arr.reduce((pre, cur) => {
      // 注意concat和push两个方法对原数组的影响
      isArray(cur) ? (pre = pre.concat(_flatten(cur))) : pre.push(cur)
      return pre
    }, [])
  }
}
let arr = [1, [2, 3, [4, 5]]]
flatten(2)(arr) // [1, 2, 3, [4, 5]]
1
2
3
4
5
6
7
8
9
10
11
12
13

# generator

function* flatten(arr) {
  // 不是数组或数组长度小于等于 1, 则直接返回
  if (!isArray(arr) || arr.length <= 1) return arr
  for (let item of arr) {
    isArray(item) ? yield* flatten(item) : yield item
  }
}
let arr = [1, [2, 3, [4, 5]]]
let g = flatten(arr)
console.log([...g]) // [1, 2, 3, 4, 5]
1
2
3
4
5
6
7
8
9
10

# 控制深度

let flatten = (depth) => {
  return function* flatten(arr) {
    depth--
    // 不是数组或数组长度小于等于 1,则直接返回
    if (!isArray(arr) || arr.length <= 1) return arr
    for (let item of arr) {
      depth <= 0 && isArray(item) ? yield* flatten(item) : yield item
    }
  }
}
let arr = [1, [2, 3, [4, 5]]]
let g = flatten(2)(arr)
console.log([...g]) // [1, 2, 3, [4, 5]]
1
2
3
4
5
6
7
8
9
10
11
12
13

# 非递归

以上的方式基本都用到了递归,最后用非递归的方式来实现

非递归的话,就需要自己手动模拟一个栈出来

function flatten(arr) {
  if (arr.length <= 1) return arr
  let res = []
  // pop方法会改变原数组,所以copy一份出来
  let stack = arr.slice()
  let item
  // 不断从栈顶拿出元素,直到栈不为空
  while ((item = stack.pop())) {
    // 如果是数组,则拍平一层再入栈,否则插入到结果数组的最前端
    isArray(item) ? stack.push(...item) : res.unshift(item)
  }
  return res
}
let arr = [1, [2, 3, [4, 5]]]
flatten(arr) // [1, 2, 3, 4, 5]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 控制深度

function flatten(arr, depth) {
  if (arr.length <= 1) return arr
  let res = []
  // pop方法会改变原数组,所以copy一份出来
  let stack = arr.slice()
  let item
  // 不断从栈顶拿出元素,直到栈不为空
  while ((item = stack.pop())) {
    // 如果是数组 & --depth >=0 ,则拍平一层再入栈,否则插入到结果数组的最前端
    if (isArray(item) && --depth >= 0) {
      stack.push(...item)
    } else {
      res.unshift(item)
    }
  }
  return res
}
let arr = [1, [2, 3, [4, 5]]]
let g = flatten(arr, 2) // [1, 2, 3, [4, 5]]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Last Updated: 10/21/2024, 4:15:17 PM