资讯专栏INFORMATION COLUMN

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

3403771864 / 252人阅读

  我们现在来说说怎么写一下数组扁平化flat(),怎么样?简单说题目就是数组扁平化(也可以叫做手动封装flat()方法),如何写好那?

  按照不同的星级进行打分: 五星打分制

  满分: ⭐⭐⭐⭐⭐

  题目实现扁平化的方法 封装 flatten

  题目描述:

  有多级嵌套数组 :[1, [2, [3, [4, 5]]], 6]将其扁平化处理 输出:[1,2,3,4,5,6]

  什么是扁平化

  定义 : 扁平化就是将多维数组变成一维数组,不存在数组的嵌套

  1. ES6 flat

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

  参数:

  depth(可选) 指定要提取嵌套数组的结构深度,默认值为 1

  返回值:

  返回一个新数组,包含数组与提取嵌套数组的所有元素的新数组

  使用Infinity,可展开任意深度的嵌套数组

  封装思路: 使用 es6 自带 API 处理 

 const arr = [1, [2, [3, [4, 5]]], 6]
  function flatten(params) {
  return params.flat(Infinity)
  }
  console.log(flatten(arr));
  // 输出: [1,2,3,4,5,6]

  得分 :

  直接使用自带的方法可以很快的实现,但这个不是合适的!

  2. toString

  如果数组的项全为数字,可以使用join(),toString()可以利用数组toString() 转为字符串 

 function flatten(arr) {
  return arr.toString().split(',').map(item =>parseFloat(item))
  }
  console.log(flatten(arr));
  // 输出:[ 1, 2, 3, 4, 5, 6 ]

  得分 :⭐ (并不是要考你的数组的方法调用)

  3. 使用正则替换

  看到嵌套的数组,如果在字符串的角度上看就是多了很多[和],如果把它们替换就可以实现简单的扁平化

  function flatten (arr) {
  console.log('JSON.stringify(arr)', typeof JSON.stringify(arr))
  let str= JSON.stringify(arr).replace(/(\[|\])/g, '');
  str = '[' + str + ']';
  arr = JSON.parse(str);
  return arr
  }
  console.log(flatten(arr))

  得分: ⭐ (并不是要考你的数组的方法调用)

  4. 循环递归

  4.1 循环 + concat + push

  当只有一层嵌套数组使用push的方式扁平化

  [1, [2, 3,4,5,6]]


  let result = [];
  for (let i = 0; i < arr2.length; i++) {
  result = result.concat((arr2[i]));
  }
  console.log(result);
  [ 1, 2, 3, 4, 5, 6 ]

  如果有多层嵌套的数组就需要使用递归的思想:

  思路

  循环判断数组的每一项是否是数组: Array.isArray(arr[i])

  是数组就递归调用上面的扁平化一层的代码 result = result.concat(flatten(arr[i]));

  不是数组,直接通过push添加到返回值数组

   function flatten(arr) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
  if (Array.isArray(arr[i])) {
  result = result.concat(flatten(arr[i]));
  } else {
  result.push(arr[i])
  }
  }
  return result
  }
  console.log(flatten(arr));

  或者使用forEach立即执行函数

 

 // 递归版本的反嵌套
  function flatten(array) {
  var flattend = [];
  (function flat(array) {
  array.forEach(function(el) {
  if (Array.isArray(el)) flat(el);
  else flattend.push(el);
  });
  })(array);
  return flattend;
  }

  当然循环可以更改成forEach循环,for of ...等其他循环,简单的循环递归就能够一样的解决啦~

  得分:⭐⭐⭐ (能够使用递归写出数组扁平化,缺少控制层级关系)

  4.2 增加参数控制扁平化深度

  这个可以理解为手写flat()方法啦~

   // forEach 遍历数组会自动跳过空元素
  const eachFlat = (arr = [], depth = 1) => {
  const result = []; // 缓存递归结果
  // 开始递归
  (function flat(arr, depth) {
  // forEach 会自动去除数组空位
  arr.forEach((item) => {
  // 控制递归深度
  if (Array.isArray(item) && depth > 0) {
  // 递归数组
  flat(item, depth - 1)
  } else {
  // 缓存元素
  result.push(item)
  }
  })
  })(arr, depth)
  // 返回递归结果
  return result;
  }
  // for of 循环不能去除数组空位,需要手动去除
  const forFlat = (arr = [], depth = 1) => {
  const result = [];
  (function flat(arr, depth) {
  for (let item of arr) {
  if (Array.isArray(item) && depth > 0) {
  flat(item, depth - 1)
  } else {
  // 去除空元素,添加非 undefined 元素
  item !== void 0 && result.push(item);
  }
  }
  })(arr, depth)
  return result;

  }

  得分:⭐⭐⭐⭐ (能够使用递归写出数组扁平化,可以通过参数控制层级关系)

  4.3 巧用 reduce

  reduce方法为数组中的每个元素按序执行一个reducer函数,每一次运行 reducer 会将先前元素的计算结构作为参数传入,最后将其结果汇总为单个返回值

  参数:

  callbackFn一个 reducer 函数,包含四个参数:

  previousVal :上一次调用callbackFn时的返回值,在第一次调用时,若指定了初始值initialValue,previousVal 的值就位 initialValue,否则初始值就是为数组的索引为 0 的元素

  currentVal:数组中正在处理的元素,在第一次调用时,若指定了初始值,其值则为数组索引为 0 的元素 array[0],否则为 array[1]

  currentIndex: 数组中正在处理的元素的索引,若指定了初始值 initialValue,则起始索引号为 0,否则从索引 1 起始

  array : 用于遍历的数组

  initialValue(可选) : 作为第一次调用 callback 函数时参数 previousValue 的值

  返回值: 使用 reducer 回调函数遍历整个数组后的结果

  思路:

  当我们使用 reduce 来解析第一层数组,可以得到:

  const arr = [1, [[2, 3], 4],5]
  const result = arr.reduce((acc, val) => acc.concat(val), []);
  console.log(result);
  // 输出: [1,[2,3],4,5]

  可以看出上面的代码可以扁平化一层数组,对于多层级嵌套的数组, 这个时候就需要使用递归的思想来解决问题了,再次遍历数组,发现数组元素任然是数组的时候,再次执行上面扁平化

  手写方法

  const arr = [1, [[2, 3], 4],5]
  function flatten (arr,deep=1) {
  return arr.reduce((acc,val) => acc.concat(Array.isArray(val)? flatten(val,deep-1):val),[])
  }
  console.log(arr, Infinity);
  // 输出:[ 1, 2, 3, 4, 5, 6 ]

  得分:⭐⭐⭐⭐ (能够使用递归写出数组扁平化,巧用reduce方法可加分)

  4.4 使用 Generator 函数

  GeneratorFunction是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。

  它不同于普通函数,是可以暂停执行的,所以函数名之前要加星号,以示区别。

  整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用 yield 语句注明。Generator 函数的执行方法如下。

  构造器生成新的生成器函数

  function* flatten(array) {
  for (const item of array) {
  if (Array.isArray(item)) {
  yield* flatten(item);
  } else {
  yield item;
  }
  }
  }

  得分:⭐⭐⭐ (使用Generator 函数 进行递归)

  5. 使用堆栈 stack 避免递归

  递归循环都可通过维护一个堆结构来解决

  如果不使用递归数组来实现扁平化,可以使用堆栈来解决

  深度的控制比较低效,因为需要检查每一个值的深度

  思路:

  把数组通过一个栈来维护

  当栈不为空的时候循环执行处理

  pop()将栈尾出栈

  如果出栈的元素是数组,就将该元素解构后每一元素进行入栈操作

  出栈的元素不是数组就push进返回值res

  反转恢复原数组的顺序

  var arr1 = [1,2,3,[1,2,3,4, [2,3,4]]];
  function flatten(arr) {
  const stack = [...arr];
  const res = [];
  while (stack.length) {
  // 使用 pop 从 stack 中取出并移除值
  const next = stack.pop();
  if (Array.isArray(next)) {
  // 使用 push 送回内层数组中的元素,不会改动原始输入
  stack.push(...next);
  } else {
  res.push(next);
  }
  }
  // 反转恢复原数组的顺序
  return res.reverse();
  }
  flatten(arr1);// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]

  得分:⭐⭐⭐⭐ (使用数据结构栈的特性取代递归操作,减少时间复杂度)

  6.while 循环+ some方法

  some:方法测试数组中是不是至少有 1 个元素通过了被提供的函数测试。它返回的是一个 Boolean 类型的值。

  思路

  通过some来判断数组中是否用数组,通过while不断循环执行判断, 如果是数组的话可以使用 拓展运算符... ... 每次只能展开最外层的数组,加上contact来减少嵌套层数,

 

 function flatten(arr) {
  while (arr.some(item=&gt; Array.isArray(item))) {
  console.log(...arr)
  arr = [].concat(...arr)
  console.log(arr)
  }
  return arr
  }
  console.log(flatten(arr));

  得分:⭐⭐⭐⭐ (使用while循环取消递归操作, 巧用some操作进行判断)

      全文内容已讲述完毕,欢迎大家后续关注更多精彩内容。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/128174.html

相关文章

  • 记录一次搜狐面试(包括笔试题)

    摘要:协议主要是通过请求头当中的一些字段来和服务器进行通信,从而采用不同的缓存策略。会缓存所有的结构,但是问题在于,一些页面开始时进行的上报或者请求可能会被影响。 1.如何用一个div实现下图 showImg(https://segmentfault.com/img/bVbm8Rf?w=133&h=134); css #demo{ width:120px; h...

    tianhang 评论0 收藏0
  • 记录一次搜狐面试(包括笔试题)

    摘要:协议主要是通过请求头当中的一些字段来和服务器进行通信,从而采用不同的缓存策略。会缓存所有的结构,但是问题在于,一些页面开始时进行的上报或者请求可能会被影响。 1.如何用一个div实现下图 showImg(https://segmentfault.com/img/bVbm8Rf?w=133&h=134); css #demo{ width:120px; h...

    bladefury 评论0 收藏0
  • 记录一次搜狐面试(包括笔试题)

    摘要:协议主要是通过请求头当中的一些字段来和服务器进行通信,从而采用不同的缓存策略。会缓存所有的结构,但是问题在于,一些页面开始时进行的上报或者请求可能会被影响。 1.如何用一个div实现下图 showImg(https://segmentfault.com/img/bVbm8Rf?w=133&h=134); css #demo{ width:120px; h...

    rozbo 评论0 收藏0
  • 前端JS面试

    摘要:前端面试题精选函数实现应用首先什么是函数,直译记忆,缓存等意思,到了计算机层面就翻译为缓存函数,缓存函数就是把计算的结果,存在函数中,当再次调用的时候就可以直接调用。 前端面试题精选 1.memorize函数实现应用 首先什么是memorize函数,memorize直译:记忆,缓存等意思,到了计算机层面就翻译为缓存函数,缓存函数就是把计算的结果,存在函数中,当再次调用的时候就可以直接调...

    bawn 评论0 收藏0
  • JS数组的几个不low操作(3)

    摘要:序列文章面试之函数面试之对象前言本文主要从应用来讲数组的一些骚操作如一行代码扁平化维数组数组去重求数组最大值数组求和排序对象和数组的转化等上面这些应用场景你可以用一行代码实现扁平化维数组终极篇是扁平数组的表示维度值为时维度为无限大开始篇实质 showImg(https://segmentfault.com/img/bVbpRMS?w=1858&h=1286); 序列文章 JS面试之函数...

    fish 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<