面试热点:数组扁平化的五种方法

594 阅读9分钟

前言

在咱们面试的过程中,很容易会被面试官问到这样一道题:请问你如何实现一个数组的扁平化呢?就是给你一个多维数组像[1, 2, [3, 4, [5]]],你如何将它实现扁平化后输出[1,2,3,4,5]呢?在解决这个问题之前,我来给大家解释一下啥叫数组扁平化,比如我们在公司写前端项目时通过接口请求向后端要数据,后端给你返回了一个数据,你很开心,但是后端给你返回的数据就长上面这样[1, 2, [3, 4, [5]]],其实你希望的是后端返回一个12345装在一个数组里面,当然这里咱们只是用数字举个例子,可能数组里面的元素不是数字是其他啥项目的数据,不重要,这个时候你要么就去找后端叫他修改好来,要么就自己修改好来,但是很多时候后端的数据也不是他们写的而是来自第三方平台的数据,那么这个时候就得咱们自己来处理了,将这个多维数组变成一维数组,就是数组的扁平化。

数组扁平化是什么?

数组扁平化,又称为数组降维,是一种将多维数组(即数组中包含其他数组作为元素的数组)转换为一维数组(所有元素都是基本数据类型,不再包含子数组)的过程。简单来说,就是将嵌套的数组结构摊平,使得所有的元素都处于同一个层次上。

如何实现数组的扁平化呢?

方法一:flat()方法

flat() 方法是官方自己打造的一种数组扁平化方法,可以将一个多维数组变成你想要的一维数组.

const arr = [1, 2, [3, 4, [5]]];
//数组的扁平化
const newArr = arr.flat();
console.log(newArr); 

image-20240522232306187.png

可以看到它降了一维从三维数组变为了二维数组,你说可是我想要的是一维数组呀,其实是flat()方法括号里面的参数的原因,flat(1)写参数一就代表降一维,写flat(2)就代表降两维,不写就代表默认降一维,

此时你可能脑海里萌生一个想法,如果我传一个3进去会怎么样?会不会把数组降成一维都没有呢?你说这值每增大一位就像多砍一刀,那会不会砍没了呢?咱们来试试

const arr = [1, 2, [3, 4, [5]]];
const newArr = arr.flat(3);
console.log(newArr); 

image-20240522233509321.png

还是一维,所以他不会在降了,不管你写多大都没关系。可是在实际开发中你也不知道括号里面写几才能把它降为一维,如果一个数组里面嵌套了很多子数组,那我咋知道需要降几维呀?别急,这里咱们就用flat(Infinity),这个就代表不管你是几维数组,我都给你降成一维数组,咱们试试看:

const arr = [1, 2, [3, 4, [5]]];
const newArr = arr.flat(Infinity);//flat 括号里的数字代表降几维
console.log(newArr); 

image-20240522231731692.png

咦,它真的降成一维了嘞,牛!这个方法是官方自带的方法,确实很好用,可是面试的时候面试官问你你回答这个方法,那他肯定还会问你如果不使用这个方法还能怎么实现呢?因为这个是api自带的方法,他考察不到你的编程能力呀。所以还有别的方法呢?所以这时就要用递归来解决了

方法二:递归

递归是一种常见的实现数组扁平化的方法。它的思路是通过递归遍历数组里的所有元素,如果遇到数组,则继续递归处理,直到将所有元素都放入一个新的一维数组中。具体实现方式有直接递归和reduce()方法两种。

直接递归:

直接递归的实现就是通过循环遍历数组元素,判断当前元素是否为数组,如果是,则递归调用扁平化函数,如果不是,则将其加入到新的一维数组中。

const arr = [1, 2, [3, 4, [5]]];
//使用递归来解决
function flatten(arr) {
    let res = [];
    for (let i = 0; i < arr.length; i++) {
        if (Array.isArray(arr[i])) {
            //res=res.concat(flatten(arr[i]));
            res = [...res, ...flatten(arr[i])]
        } else {
            res.push(arr[i]);
        }
    }
    return res;
}
const newArr = flatten(arr);
console.log(newArr);
  1. 在循环体内,用Array.isArray(arr[i])判断当前元素是否为数组。

    如果当前元素arr[i]是一个数组,则对该子数组进行递归调用flatten(arr[i]),将扁平化后的结果与res数组合并。这里可以使用扩展运算符...来展幵数组,等价于使用res = res.concat(flatten(arr[i]))。这两种方法效果是一样的。

    如果当前元素不是一个数组,则直接将该元素arr[i] push到结果数组res中。

递归 配合reduce()

reduce()方法也可以遍历数组的所有元素,还能将他们累加起来

const arr = [1, 2, [3, 4, [5]]];
function flatten(arr) {
   return arr.reduce((pre,item) => {
    return pre.concat(Array.isArray(item)? flatten(item) : item)//[1,2],[3,4],[5]
    },[])
}
console.log(flatten(arr));

flatten函数内部,使用了数组的reduce方法。reduce方法会对数组中的每个元素执行一个提供的 reducer 函数(升序执行),最终结果是将数组简化为单一的输出值。reduce 的目的是将多维数组扁平化为一维数组。

  • reduce的第一个参数是一个回调函数,该回调函数接收两个参数:累计器pre(previousValue)和当前元素item(currentValue)。

  • 回调函数内部,首先检查item是否为数组(Array.isArray(item)`):

    如果item是数组,就对这个子数组递归调用flatten函数,然后再与累计器pre进行拼接(pre.concat(flatten(item)))。这样可以处理任意深度的嵌套数组。

    如果item不是一个数组,直接将其添加到累计器pre中。

  • reduce的第二个参数是一个初始值[],即累计器pre的初始状态,表示扁平化结果的起始为空数组。

那么数组扁平化还有没有别的方法呢?这时候体现你能力水平的时候就到了,面试官肯定不会因为你答了递归就这样放过你,就结束了,递归是基本所有面试者都要能写上来的,答不上可能就直接就被pass掉了呀。所有扁平化方法不管什么投机取巧,别人想不到的你都要能用上,你都要知道,并不是说你会一种就够了,面试官问你还有吗,此时别的面试者可能就会觉得我用的最多的就是这两种,想不出来了,唉,此时如果你能跟面试官得得得的在说一大堆,在这个问题上你就可以框框得分。这里有一个十分投机取巧的方法,就是数组里面的toString()方法.

方法四:toString()

还记得我们之前说过数组调用tostring()方法会得到什么吗?会得到由数组内部元素以,的形式进行拼接的一个字符串,所以当我们直接对这个数组用toString()方法是不是可以得到一个“1,2,3,4,5”组成的字符串,我们在对它用splic()方法转化为数组后再将里面的字符转化为数字是不是也可以得到一个一维数组。

const arr = [1, 2, [3, 4, [5]]];
newarr=arr.toString()
newarr2=newarr.split(',')
newarr3=newarr2.map(Number)   
console.log(newarr3);
  1. 使用toString()方法将数组转换为字符串。arr.toString()会将数组转换成一个以逗号分隔各个元素的字符串,对于嵌套数组,它会将内部数组视为一个整体,转换结果为"1,2,3,4,5"
  2. 然后,使用split(',')方法将得到的字符串以逗号为分隔符切分成数组,得到newarr2["1", "2", "3", "4", "5"]
  3. 接下来,使用map(Number)方法遍历数组newarr2,并将每个元素转换为数字类型。map方法会对数组中的每个元素应用一个函数,这里应用的是构造函数Number作为函数,可以将字符串数字转换为实际的数字类型,因此newarr3变为[1, 2, 3, 4, 5]

但是这种方法只限于数组中只有数字

如果数组中有其他元素像字符“abc”的话他就不行了

const arr = [1, 2,"abc", [3, 4, [5]]];
newarr=arr.toString()
newarr2=newarr.split(',')
newarr3=newarr2.map(Number)   
console.log(newarr3);

image-20240523090635450.png

你看,当数组中有字符串时,在最后一步,将字符“abc”转化为数字时会返回NaN,所以就行不通了

所它只适合数组中只有数字的情况。

方法五:解构

在js中,解构是一种特殊的语法,允许你将数组或对象的属性直接分解到不同的变量中。这种特性让代码更加简洁、易读,同时也简化了数据的访问方式。解构赋值是ES6引入的一个重要特性。

利用解构语法实现数组扁平化的思路是通过while循环遍历数组,并使用解构语法将第一个元素与剩余元素分离开。如果分离后的第一个元素仍为数组,则使用解构语法继续分离,否则将其加入到新的一维数组中。

使用some()+concat()

const arr = [1, 2, [3, 4, [5]]];
function flatten(arr) {
    while(arr.some(item=>Array.isArray(item))){
        arr=[].concat(...arr);//[].concat(1,2,[3,4,[5]])   => [1,2,3,4,[5]]
    }
    return arr;
}
console.log(flatten(arr));

image-20240523101221650.png

  • while(arr.some(item=>Array.isArray(item))) { ... }:使用while循环,条件是检查数组中是否存在至少一个元素还是数组类型。这意味着只要arr中还包含子数组,循环就会继续。
  • arr=[].concat(...arr);:这一行展平数组。[].concat(...arr)的操作会把arr中的所有元素展开并合并到一个新的数组中。

只要while()循环一次就会降一维,降了一维之后如果发现里面还有数组继续while()循环,又降一维,直至没有嵌套数组了,这个方法也是极致的优雅。

屏幕前的你是否还有关于数组扁平化的方法呢?欢迎评论区留言......