JavaScript面试之如何实现数组拍平(扁平化)方法

目录
  • 1 什么叫数组拍平?
  • 2 JS标准库中的数组拍平方法
  • 3 实现一个flat方法
    • 3.1 如何遍历一个数组
    • 3.2 如何判断元素是否为数组
    • 3.3 递归
    • 3.4 初步实现flat方法
  • 4 优化
    • 4.1 指定展开深度
    • 4.2 数组空位处理
      • 4.2.1 for...of增加空位判断
      • 4.2.2 forEach、map方法遍历
      • 4.2.3 reduce方法
  • 5 其他
    • 5.1 栈
    • 5.2 改进
  • 总结

1 什么叫数组拍平?

概念很简单,意思是将一个“多维”数组降维,比如:

// 原数组是一个“三维”数组
const array = [1, 2, [3, 4, [5, 6], 7], 8, 9]

// 可以降成二维
newArray1 = [1, 2, 3, 4, [5, 6], 7, 8, 9]

// 也可以降成一维
newArray2 = [1, 2, 3, 4, 5, 6, 7, 8, 9]

数组拍平也称数组扁平化、数组降维。

2 JS标准库中的数组拍平方法

JavaScript标准库中已经实现了数组拍平方法Array.prototype.flat()

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

语法:var newArray = arr.flat([depth])

参数:depth为可选值,表示要遍历多维数组的深度,默认值为1。可以理解为想要展开(或者说降维)的层数。

返回值:遍历到的元素和子数组的元素组合成的新数组

举例:

const array = [1, 2, [3, 4, [5, 6], 7], 8, 9]
const newArray1 = array.flat() // 等价于array.flat(1);降1维
// newArray1: [1, 2, 3, 4, [ 5, 6 ], 7, 8, 9]

const newArray2 = array.flat(2) // 降2维
// newArray2:[1, 2, 3, 4, 5, 6, 7, 8, 9]

特殊:

depth<=0时,返回的数组和原数组维数一样(注意只是维数一样,空位情况见第3点)

const array = [1, 2, [3, 4, [5, 6], 7], 8, 9]
array.flat(-1)
// [1, 2, [3, 4, [5, 6], 7], 8, 9]

depth=Infinity,返回的数组变成一维

array.flat(Infinity)
// [1, 2, 3, 4, 5, 6, 7, 8, 9]

原数组有空位,flat方法会消除空位,即使是flat(0)也会消除空位,所以第1点说的是“只是维数一样”。并且flat方法展开到哪一层,空位就会消除到哪一层,再深层的空位不会消除

const array1 = [1, , 2, [3, ,4, [5, 6], 7], 8, 9]
// 注意这个数组有两个空位
array.flat(0)
// [ 1, 2, [ 3,  ,4, [ 5, 6 ], 7 ], 8, 9 ]
// 第一个空位没了,第二个空位还在

3 实现一个flat方法

flat方法展开一层(降1维)的步骤:遍历数组,判断当前元素是否为数组,如果不是数组,直接保存;如果是数组,将其展开后保存

flat方法展开多层(降多维)无非就是在展开一层的基础上,使用递归将数组子元素进行同样的操作。

可以将这个方法拆分成三步:

1、如何遍历数组

2、如何判断元素是否为数组

3、递归

实现上述三步,将他们组合起来就可以得到不同的flat实现

3.1 如何遍历一个数组

方法特别多,这里介绍3类:

1、for相关

  • for 循环
  • for...of

for...in是为遍历对象属性而构建的,不建议与数组一起使用

const array = [1, 2, [3, 4, [5, 6], 7], 8, 9]
// for循环
for (let i = 0; i < array.length; i++) {
    const element = array[i];
}
// for...of
for (const element of array) {

}

2、数组方法:能直接取到数组元素的方法

  • forEach()
  • reduce()
  • map()
// forEach()
array.forEach(element => {

});
// reduce()
array.reduce((pre, cur) => {
    const element = cur
}, [])
// map()
array.map(element => {

})

3、数组方法:返回遍历器(Iterator)对象的方法

  • keys()
  • values()
  • entries()
// 这三种方式仅仅是获得遍历器对象,还需搭配for...of来进行遍历
// keys()
for (let i of array.keys()) {
  const element = array[i]
}
// values()
for (let element of array.values() ) {

}
// entries()
for (let [i, element] of array.entries()) {
  console.log(array[i])
  console.log(element)
}

3.2 如何判断元素是否为数组

设有一变量a,判断其是否为数组。这里提供4种方法:

  • Array有一个静态方法Array.isArray()用于判断某个变量是否是一个数组
  • instanceof运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
    若a是数组,则其原型链上会出现Array.prototype
  • 通过对象的constructor判断(此方法可能失效,因为constructor可以手动更改)
  • 通过Object.prototype.toString()来判断,该方法可以返回一个表示该对象的字符串
// 方法1
Array.isArray(a)
// 方法2
a instanceof Array
// 方法3
a.constructor === Array
// 方法4
// 使用call来调用Object.prototype上的toString方法
Object.prototype.toString.call(a) === '[object Array]'

// 不能这么判断,因为这个toString已经覆盖了Object.prototype.toString
// 只有Object.prototype.toString能正确判断类型
a.toString()

3.3 递归

递归:对子元素进行同样的操作

function flat() {
  let res = []
  遍历数组 {
    if (当前元素是数组) {
      flat(当前元素)得到一维数组
      将一维数组拼接到res中
    } else {
      res.push(当前元素)
    }
  }
  return res
}

3.4 初步实现flat方法

挑选遍历方式和判断数组的方式,搭配递归就可以初步实现flat方法,如:

function myFlat(arr) {
  let res = [];
  for (const item of arr) {
    if (Array.isArray(item)) {
      res = res.concat(myFlat(item));
      // 注意concat方法返回一个新数组,不会改变原数组
    } else {
      res.push(item);
    }
  }
  return res;
}

myFlat方法可以实现将"多维"数组拉平成一维数组,但是不能指定展开深度depth,并且也无法处理数组空位

4 优化

4.1 指定展开深度

处理展开深度其实很简单,我们可以增设一个递归终止条件,即depth<=0,代码如下:

function myFlat(arr, depth = 1) {
  // 若depth<=0,则直接返回
  if (depth <= 0) {
      return arr
  }
  let res = [];
  for (const item of arr) {
    if (Array.isArray(item)) {
      // 每次递归调用,将depth-1
      res = res.concat(myFlat(item, depth - 1));
    } else {
      res.push(item);
    }
  }
  return res;
}

4.2 数组空位处理

事实上我们应该尽量避免出现数组空位的情况

前面我们提到了遍历数组的不同方法,它们对于数组空位的处理不尽相同

其中forEach、reduce、map遍历时遇到空位会直接忽略;而for...of则不会忽略,它遇到空位会将其当作undefined处理

4.2.1 for...of增加空位判断

因此我们需要改进for...of遍历数组的myFlat方法:

function myFlat(arr, depth = 1) {
  if (depth <= 0) {
    return arr;
  }
  let res = [];
  for (const item of arr) {
    if (Array.isArray(item)) {
      res = res.concat(myFlat(item, depth - 1));
    } else {
      // 判断数组空位
      item !== undefined && res.push(item);
    }
  }
  return res;
}

4.2.2 forEach、map方法遍历

当然也可以使用forEach、map方法来遍历数组,这样就不用手动判断了

但是这里有一个特殊情况需要考虑,就是当depth <= 0时,我们用filter方法来消除数组空位

// forEach
function myFlat(arr, depth = 1) {
  if (depth <= 0) {
    return arr.filter(item => item !== undefined);
  }
  let res = [];
  arr.forEach((item) => {
    if (Array.isArray(item)) {
      res = res.concat(myFlat(item, depth - 1));
    } else {
      res.push(item);
    }
  });
  return res;
}

// map
function myFlat(arr, depth = 1) {
  if (depth <= 0) {
    return arr.filter(item => item !== undefined);
  }
  let res = [];
  arr.map((item) => {
    if (Array.isArray(item)) {
      res = res.concat(myFlat(item, depth - 1));
    } else {
      res.push(item);
    }
  });
  return res;
}

4.2.3 reduce方法

其中,使用reduce方法实现的最为简洁,也是面试中常考的方法之一

function myFlat(arr, depth = 1) {
  return depth > 0
    ? arr.reduce(
        (pre, cur) =>
          pre.concat(Array.isArray(cur) ? myFlat(cur, depth - 1) : cur),
        []
      )
    : arr.filter((item) => item !== undefined);
}

5 其他

5.1 栈

理论上,递归方法通常可以转换成非递归方法,即使用栈

function myFlat(arr) {
  let res = [];
  const stack = [].concat(arr);
  while (stack.length > 0) {
    const item = stack.pop();
    if (Array.isArray(item)) {
      // 用扩展运算符展开一层
      stack.push(...item);
    } else {
      item !== undefined && res.unshift(item);
    }
  }
  return res;
}

但是此方法不能指定展开深度,只能彻底展开成一维数组

5.2 改进

针对栈不能指定展开深度的缺点进行改进,代码如下:

function myFlat(arr, depth = 1) {
  if (depth <= 0) {
    return arr.filter((item) => item !== undefined);
  }
  let res;
  let queue = [].concat(arr);
  while (depth > 0) {
    res = [];
    queue.forEach((item) => {
      if (Array.isArray(item)) {
        // 注意用扩展运算符将数组展开前先用filter方法去掉空位
        res.push(...item.filter((e) => e !== undefined));
      } else {
        res.push(item);
      }
    });
    depth--;
    queue = res;
  }
  return res;
}

总结

到此这篇关于JavaScript面试之如何实现数组拍平(扁平化)方法的文章就介绍到这了,更多相关JS实现数组扁平化方法内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JS数组中常用方法技巧学会进阶成为大佬

    目录 splice()方法 join()方法 reverse()方法 every()方法 reduce()方法 filter()方法 findIndex()方法 和find()方法 findIndex() find() forEach()方法 some()方法 indexOf()方法 sort ()方法 push()方法 pop()方法 unshift()方法 shift()方法 splice()方法 截取替换数组 第一个参数是起始位置,第二个是截取的个数,第三是替换的元素 ,返回值是截取的元素

  • js数组的 entries() 获取迭代方法

    目录 1.entires() 方法语法详解 2.entires() 方法常见使用及注意 2.1 返回迭代器对象 2.2 for...of...的使用 2.3 二维数组行排序 1.entires() 方法语法详解 entries() 方法返回一个数组的迭代对象,该对象包含数组的键值对 (key/value) . 迭代对象中数组的索引值作为 key, 数组元素作为 value.它的原型(__proto__:Array Iterator)上有一个next方法,可用用于遍历迭代器取得原数组的[key,v

  • JavaScript数组 几个常用方法总结

    目录 1.前言 2.filter() 3.map() 4.sort() 5.reduce() 6.forEach() 7.方法列表 8.本文参考 1.前言 数组方法有太多了,本文记录一些本人容易记错的一些数组方法,用于巩固及复习. 后续会慢慢的将其他数组方法添加进来. 善用数组方法可以使数据处理变的优雅且简单. 那下面让我们开始吧: 2.filter() 描述: filter() 方法创建一个新数组,并将符合条件的所有元素添加到创建的新数组中. 语法: Array.filter(callback

  • 浅谈JS数组内置遍历方法有哪些和区别

    目录 forEach()(ES6)方法 map()(ES6) 方法 flatMap()方法 for...in... for...of... filter(ES6)遍历数组 every()函数(ES6) find()函数(ES6) findIndex()函数 (ES6) forEach()(ES6)方法 forEach()(ES6)方法对数组的每个元素执行一次给定的函数. 1. 数组里的元素个数有几个,该方法里的回调就会执行几次     2. 第一个参数是数组里的元素,第二个参数为数组里元素的索引

  • js 数组 fill() 填充方法

    目录 1.fill()  语法 2.fill() 的使用 3.总结 前言: 我们知道了很多了初始化数组的方法,但是初始化数组之后,数组中的每一项元素默认为 empty 空位占位,如何对数组这些空位添加默认的元素,ES6提供了 fill() 方法实现这一操作.本文总结数组 fill() 方法的详细使用. 1.fill()  语法 fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素.不包括终止索引.返回修改后的原始数组,不创建新数组. 使用语法:array.fill( va

  • JavaScript数组去重的几种方法详谈

    目录 一.Set去重 二.双重for循环去重 三.利用indexOf去重 四.利用icludes去重 五.filter 六.Map 总结 一.Set去重 function funSet(arr){ return Array.from(new Set(arr)); } 二.双重for循环去重 function funFor(arr){ for(let i=0,len=arr.length;i<len;i++){ for(let j=i+1,len=arr.length;j<len;j++){ i

  • JS数组方法some、every和find的使用详情

    目录 1.some 2.every 3.find 1.some 在MDN中这样定义:some()方法测试数组中是不是至少有1个元素通过了被提供的函数测试.它返回的是一个Boolean类型的值. 简而言之就是:它对数组中的每一项进行校验,只要有一项通过了就是true. 它只会返回true或false 它会对数组中的每一项进行检测,千万不要在some里面进行if else操作 不要在return后面写true或false,return后面跟的是你的条件 最近做后台管理系统遇到一个需求:弹出一个Dia

  • JavaScript数组Array的一些常用方法总结

    目录 一.JavaScript中创建数组的方式 二.数组方法概括 三.方法详解 1.join() 2.pop() 3.shift() 4.push() 5.unshift() 6.concat() 7.slice() 8.sort() 9.reverse() 10.toString() 11.splice() 12.valueOf() 13.indexOf() 14.lastIndexOf() 15.forEach() 16.map() 17.filter() 18.every() 19.som

  • JavaScript面试之如何实现数组拍平(扁平化)方法

    目录 1 什么叫数组拍平? 2 JS标准库中的数组拍平方法 3 实现一个flat方法 3.1 如何遍历一个数组 3.2 如何判断元素是否为数组 3.3 递归 3.4 初步实现flat方法 4 优化 4.1 指定展开深度 4.2 数组空位处理 4.2.1 for...of增加空位判断 4.2.2 forEach.map方法遍历 4.2.3 reduce方法 5 其他 5.1 栈 5.2 改进 总结 1 什么叫数组拍平? 概念很简单,意思是将一个"多维"数组降维,比如: // 原数组是一个

  • javascript数组拍平方法总结

    在开发过程中有得时候总是碰一些共性的问题,比如将一个二维数组拍平成一维数组,或者三维数组拍平成一维数组.这些问题在遇到的时候总会重新思考,不如将其提炼出来,总结一下. 下面笔者将为大家演示一下,将一个多维数组拍平成一个一维数组的两种方法,算是抛砖引玉,大家有更好的方法可以在留言区发表. 首先是第一种方法,递归处理,代码如下: var arr = [1, 2, 3, [3, 3, 3, [5, 4, 5, 6, 6, 7, 8]], [333, 4444] ]; function product(

  • JavaScript通过join函数连接数组里所有元素的方法

    本文实例讲述了JavaScript通过join函数连接数组里所有元素的方法.分享给大家供大家参考.具体实现方法如下: <script type="text/javascript"> var days = ["Sunday","Monday","Tuesday","Wednesday", "Thursday","Friday","Saturday&

  • JavaScript数组去重和扁平化函数介绍

    目录 1.数组扁平化(又称数组降维) 方法一:使用 reduce 方法 方法二:栈 2.数组去重 方式一:Set(ES6) 方式二:reduce 方法三:filter 1.数组扁平化(又称数组降维) flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回 const test = ["a", ["b", "c"], ["d", ["e", ["

  • Javascript中数组去重与拍平的方法示例

    数组的判断 在说如何进行数组的去重和拍平之前,先说一下怎么判断数组,因为要进行数组的处理当然要先判断下传过来的数据是不是数组. 首先我们都知道js的数据类型只有5种,分别是Undefined.Null.Boolean.Number和String,数组只是一个对象,用typeof([])返回的结果知识一个Object的字符串,因此我们需要通过其他手段来判断它,这里就说两种方法. 第一种用instenceof方法 instanceof是ES5提供的一个方法,它可以用来判断实例是否是某个类的实例,例如

  • JS前端面试数组扁平化手写flat函数示例

    目录 前言 题目 实现扁平化的方法 封装 flatten 1. ES6 flat 2. toString 3. 使用正则替换 4. 循环递归 4.1 循环 + concat + push 4.2 增加参数控制扁平化深度 4.3 巧用 reduce 4.4 使用 Generator 函数 5. 使用堆栈 stack 避免递归 6.while 循环+ some方法 前言 由于上半年参加了校招的技术面试, 前前后后面了20多个人了, 每次面试都会让应聘者手写一下数组扁平化flat(),但是发现居然没有

  • Js数组扁平化实现方法代码总汇

    题目: 请写出一个数组拍平函数.效果如下: var arr=['a', ['b', 'c'], 2, ['d', 'e', 'f'], 'g', 3, 4]; flat(arr) //a,b,c,2,d,e,f,g,3,4 方法一:使用toString方法先将arr转换为一个字符串, 再以split分割为数组,再将数组里面的元素转换为数字类型 var arr =['a', ['b', 'c'], 2, ['d', 'e', 'f'], 'g', 3, 4]; function flat(arr)

  • JavaScript面试数组index和对象key问题详解

    目录 面试题一: 1.数组赋值 2.数组取值 面试题二: 1.对象赋值 2.对象取值 总结 面试题一: var arr = [1, 2, 3, 4] 复制代码 问:arr[1] = ?; arr['1'] = ? 答:arr[1] = 2; arr['1'] = 2 这里可以再分为两个问题: 1.数组赋值 var arr = [1, 2, 3, 4] arr[1] = 10; // 数字场景 arr['10'] = 1; // 字符串场景 arr['a'] = 1; // 字符串场景 arr[t

  • JavaScript面试技巧之数组的一些不low操作

    前言 本文主要从应用来讲数组api的一些骚操作; 如一行代码扁平化n维数组.数组去重.求数组最大值.数组求和.排序.对象和数组的转化等: 上面这些应用场景你可以用一行代码实现? 1.扁平化n维数组 1.终极篇 [1,[2,3]].flat(2) //[1,2,3] [1,[2,3,[4,5]].flat(3) //[1,2,3,4,5] [1,[2,3,[4,5]]].toString() //'1,2,3,4,5' [1[2,3,[4,5[...]].flat(Infinity) //[1,2

  • Javascript面试经典套路reduce函数查重

    今天在偶然间查看到了一段代码,代码使用了很短的篇幅完成了字符串统计相同字符次数这个经典面试题,其中用到了reduce这个方法,网上查了查,没有查到什么有价值的东西,导致浪费了我一些时间才看懂,现将我的思路整理如下: 原代码: var arr="qweqrq" var info= arr.split('').reduce((a,b)=> (a[b]++ || (a[b]=1),a) ,{}) console.log(info) 代码思路是这样的,先将字符串arr通过split方法切

随机推荐