一文搞懂数组扁平化:从递归思想到五大实用方法
前言
在很多情况下处理数据都需要将多维数组转换成一维数组,这个过程就被称为数组的扁平化,这个也是在面试的时候常常被问到的一个点,下面将探讨五种实现数组扁平化的方法。数组的扁平化会使用到递归,所以下面将先简单介绍一下递归思想。
递归思想
递归的核心原理是函数或过程在定义中直接或间接地调用自身
实例一:实现5的阶乘
- 用for循环实现:
赋值res为1,循环第一遍时res = 1 * 2,返回res的值为2,循环第二遍时res = 2 * 3,返回res的值为6,直到循环到第4遍时,res = 24 * 5,循环结束。
function mul(num){
let res = 1;
for(let i = 2;i <= num;i++){
res = res * i;
}
return res;
}
console.log(mul(5));// 输出 120
- 用递归思想实现:
一、找公式:mul(num) = num * mul(num - 1);
mul(5) = 5 * mul(4);
mul(4) = 4 * mul(3);
mul(3) = 3 * mul(2);
mul(2) = 2 * mul(1);
二、找出口:当 num 为 1 或者 num 为 0 时,返回值为 1,不然会一直递归下去。
function mul(num){
if(num === 1 || num === 0){
return 1;// 找出口
}else{
return num * mul(num - 1);// 找公式
}
}
console.log(mul(1));// 输出 1
console.log(mul(5));// 输出 120
实例二:斐波那契数列
斐波那契数列:1,1,2,3,5,8,13,21,34,55,89…… ,从第三项开始,每一项都等于前两项之和,这是一个线性递推数列。fib(0)=1, fib(1)=1, fib(n)=fib(n-1)+fib(n-2) (n>=2)
一、找公式:fib(n)=fib(n-1)+fib(n-2) (n>=2)
二、找出口:当 n 为 0 或者 n 为 1 时,返回值为1
function fib(n) {
if(n === 0 || n === 1) return 1;// 找出口
return fib(n - 1) + fib(n - 2);// 找公式
}
console.log(fib(9)); // 55
数组扁平化
方法一 使用flat方法
数组自带的flat(depth)方法是一个用于将多维数组扁平化为一维数组的方法。该方法不会改变原数组,而是返回一个新的数组。
其中的参数depth
表示要展平的深度,默认为 1
。如果设置为 Infinity
,则会完全展平数组,不论嵌套多少层。
let arr = [1, 2, [3, 4, [5, 6]]];
let newArr = arr.flat(); // 默认情况下,只展开一层
console.log(newArr); // 输出: [1, 2, 3, 4, [5, 6]]
// 指定深度
let Arr = arr.flat(Infinity);
console.log(Arr); // 输出: [1, 2, 3, 4, 5, 6]
方法二 使用递归
思路:
遍历数组,创建一个空数组 let res = []
,判断 a[i]
是不是数组,如果不是数组类型就将其放到新的数组中;如果判断 a[i]
是数组类型,就再遍历一遍,不是数组类型的就放到新数组中,是的话再遍历一遍,这里就是递归思维。
-
判断是不是数组类型方法有很多种,例如
arr[i] instanceof Array
,Object.prototype.toString.call(arr[i]) === '[object Array]'
,还有一个方法就是数组自带的方法Array.isArray(arr[i])
,这里我使用的是数组自带的方法,更简洁一点。 -
通过递归,res得到三个新的数组,分别是 [1,2] [3,4] [5,6],要将这些数组合并到一起返回一个新数组,有两种方法实现。
const arr = [1, 2, [3, 4, [5, 6]]];
function flatten(arr) {
let res = []; // [1,2] [3,4] [5,6]
for (let i = 0; i < arr.length; i++) {
if(Array.isArray(arr[i])){// 判断 arr[i] 是不是数组类型
res = res.concat(flatten(arr[i]));// 使用concat方法合并数组
}else{
res.push(arr[i]);
}
}
return res;
}
const newArr = flatten(arr);
console.log(newArr);
2.数组的解构,res = [...res,...flatten(arr[i])]
合并数组。代码和上面使用concat方法基本一样,就是在合并数组那里有改变,如下所示:
if(Array.isArray(arr[i])){
res = [...res,...flatten(arr[i])]; // 通过解构方法合并数组
}
这里简单介绍一下数组的解构,使用 ...x
就可以将数组中剩余的元素提取出来并放到一个新数组中。
如下所示,a
被赋值为 1,b
被赋值为 2,而 c
是一个新数组,包含了 [3, 4, 5, 6]
,c中放的就是 arr
中除了前两个元素外的所有剩余元素。使用 ...arr
则可以直接将数组中的元素全部提取出来。
let arr = [1, 2, 3, 4, 5, 6];
let [a, b, ...c] = arr;
console.log(a, b, c);// 输出 1 2 [ 3, 4, 5, 6 ]
console.log(...arr);// 输出 1 2 3 4 5 6
...x
也可以用于合并数组,如下所示
let arr1 = [1, 2];
let arr2 = [3, 4];
let allArr = [...arr1, ...arr2]; // allArr 现在是 [1, 2, 3, 4]
console.log(allArr);
方法三 使用toSring() —— 数组中只有数字时可用
先用toSring()将数组转为字符串,再用split()将字符串转回数组,最后用map方法遍历数组,将数组里面的每一项都转回数字。
const arr = [1, 2, [3, 4, [5, 6]]];
let str = arr.toString(); // 得到字符串'1,2,3,4,5,6'
// let arr2 = str.split(','); // 得到数组['1', '2', '3', '4', '5', '6']
let newArr = str.split(',').map(item => {
return Number(item);
}); // [1, 2, 3, 4, 5, 6]
console.log(newArr);
这个方法是数组中只有数字时可以用,当数组里面有字符串时,用map方法遍历数组,将数组里面的每一项都转回数字时,Number('abc')会转为NaN。
方法四 使用reduce
JS数组中的 reduce
方法的语法如下:
arr.reduce(function(pre, item, index, arr){}, initialValue)
- arr:调用
reduce
方法的数组。 - function:执行数组中每个值的函数,该函数有以下几个参数:
pre
(累加器):累计器的当前值,是上一次回调返回的值或初始值(initialValue)。item
(当前值):数组中当前被处理的元素。index
(当前索引):可选,当前元素在数组中的索引。arr
(源数组):可选,调用reduce
的数组本身。
- initialValue(初始值):可选,作为累积器的初始值。如果未提供,则
pre
的初始值为数组的第一个元素,且数组的遍历从第二个元素开始。
reduce常用于求和,如下所示,pre的初始值为0,遍历数组,返回pre + item 的值会作为下一次pre的值,直到数组内所有元素遍历完。
let arr = [1, 2, 3, 4, 5, 6, 7];
let sum = arr.reduce((pre, item, index, arr) => {
return pre + item;
}, 0)
console.log(sum);// 输出 28
接下来用reduce方法来实现数组的扁平化
const arr = [1, 2, [3, 4, [5, 6]]];
function flatten(arr){
return arr.reduce(function(pre,item){
if(Array.isArray(item)){
return pre.concat(flatten(item));
}else{
return pre.concat(item);
}
},[])
}
console.log(flatten(arr));
将初始值设为一个空数组[],遍历数组的每一项,如果item不是数组,则直接返回pre.concat(item)
,如果判断item是数组,则返回pre.concat(flatten(item))
方法五 使用some+解构
数组中的some方法可以判断数组中有没有某个元素,只要存在某个元素就会返回true,没有这个元素就返回false,如下所示:判断数组中是否有大于4的值,有大于4的值则返回true
let arr = [1, 2, 3, 4, 5];
let res = arr.some(item => {
return item > 4;
})
console.log(res);// 输出 true
接下来用some+解构来实现数组的扁平化,通过some判断数组中是否存在任何一个元素是数组,如果数组中存在元素是数组,进入循环体,将解构后的元素放入到空数组中,返回数组,再进入循环体,直到数组中的元素不存在数组则结束循环。
const arr = [1, 2, [3, 4, [5, 6]]];
function flatten(arr){
while(arr.some(item => Array.isArray(item))){
arr = [].concat(...arr);
}
return arr;
}
console.log(flatten(arr));
结语
今天分享了递归思想和数组扁平化的五种方法,如果面试官问你怎么将数组扁平化,你应该可以很自信的回答出来了吧!
转载自:https://juejin.cn/post/7372393525651046451