数组扁平化即将多层级数组转化成一层数组。
如:
var arr = [1, 2, [3, 4]];
console.log(flatten(arr)); // [1, 2, 3, 4]
我们需要实现的就是flatten方法。
1.利用ES6的flat方法
ES6中数组新增了flat方法,会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
其中参数表示想要拉平的层数,默认为1。如果不管有多少层嵌套,都要转成一维数组,可以用Infinity关键字作为参数。
var arr = [1, 2, [3, [4, 5, [6]]]];
arr.flat(); // [1, 2, 3, [4, 5, [6]]]
arr.flat(2); // [1, 2, 3, 4, 5, [6]]
arr.flat(Infinity); // [1, 2, 3, 4, 5, 6]
// 使用 Infinity,可展开任意深度的嵌套数组
var arr1 = [1, 2, , 4, 5];
// 会将empty元素过滤掉
arr1.flat(); // [1, 2, 4, 5]
2. 使用reduce + concat + 递归
var arr=[1,2,[3, 4, [5,6,[7]]]];
function flatten(arr, deep){
return deep > 1 ? arr.reduce((prev, next) => prev.concat(Array.isArray(next) ? flatten(next, deep - 1) : next), []) : arr.slice()
}
flatten(arr, Infinity);
3.利用栈/队列 + 拓展运算符
// 也可以使用shift/unshift, 但使用pop/push速度更快
function flatten(input) {
const stack = [...input];
const res = [];
while (stack.length) {
const next = stack.pop();
if (Array.isArray(next)) {
stack.push(...next);
} else {
res.push(next);
}
}
// 反转
return res.reverse();
}
利用队列
// 使用shift/unshift
function flatten1(input) {
const stack = [...input];
const res = [];
while (stack.length) {
const next = stack.shift();
if (Array.isArray(next)) {
stack.unshift(...next);
} else {
res.push(next);
}
}
return res;
}
测试下:
// 创建整数灵活编号的列表
function range(start, stop, step) {
if (stop == null) {
stop = start || 0;
start = 0;
}
if (!step) {
step = stop < start ? -1 : 1;
}
var length = Math.max(Math.ceil((stop - start) / step), 0);
var range = Array(length);
for (var idx = 0; idx < length; idx++, start += step) {
range[idx] = start;
}
return range;
}
var arr = range(10000);
// 需要扁平化的数组
var list = [arr, [arr], [arr, [arr]]]
console.time('使用pop/push耗时');
flatten(list)
console.timeEnd('使用pop/push耗时');
console.time('使用shift/unshift耗时');
flatten1(list)
console.timeEnd('使用shift/unshift耗时');
或使用
var arr=[1,2,[3, 4, [5,6,[7]]]];
function flatten(arr){
while(arr.some(Array.isArray)){
arr = [].concat(...arr)
}
return arr;
}
4. 使用Generator函数
function* flatten(array, depth) {
if (depth === undefined) {
depth = 1;
}
for (const item of array) {
if (Array.isArray(item) && depth > 0) {
yield* flatten(item, depth - 1);
} else {
yield item;
}
}
}
const arr = [1, 2, [3, 4, [5, 6]]];
const flattened = [...flatten(arr, Infinity)];
// [1, 2, 3, 4, 5, 6]
5.使用toString + split方法(只适用于都是数字组成的数组)
var arr=[1,2,[3, 4, [5,6,[7]]]];
function flatten(arr){
return arr.toString().split(',').map(item => +item);
}
console.log(flatten(arr));
这种方法使用场景有限,如果数组是[1, '1', 2, '2']就会出问题,只适用于都是数字组成的数组。
6. 正则(不推荐)
function flatten(arr) {
let str = JSON.stringify(arr);
str = str.replace(/(\[|\])/g, '');
str = '[' + str + ']';
return JSON.parse(str);
}
7. 看看underscore1.11.0中是如何实现的:
function isArrayLike(obj) {
var length = obj == null ? void 0: obj.length
return typeof length == 'number' && length >= 0 && length <= Math.pow(2, 53) - 1;
}
function has(obj, key) {
return obj != null && Object.prototype.hasOwnProperty.call(obj, key);
}
var isArray = Array.isArray|| function (obj) {
return Object.prototype.toString.call(obj) === '[object Array]'
}
var isArguments = function (obj) {
return Object.prototype.toString.call(obj) === '[object Arguments]'
}
// IE9以下没有Arguments类型
(function() {
if (!isArguments(arguments)) {
isArguments = function(obj) {
return obj != null && Object.prototype.hasOwnProperty.call(obj, 'callee');
}
}
})();
function flatten(input, depth, strict, output) {
output = output || [];
if (!depth && depth !== 0) {
depth = Infinity;
} else if (depth <= 0) {
return output.concat(input);
}
var idx = output.length;
for (var i = 0, length = input.length; i < length; i++) {
var value = input[i];
if (isArrayLike(value) && (isArray(value) || isArguments$1(value))) {
if (depth > 1) {
flatten(value, depth - 1, strict, output);
idx = output.length;
} else {
var j = 0, len = value.length;
while (j < len) output[idx++] = value[j++];
}
} else if (!strict) {
output[idx++] = value;
}
}
return output;
}
flatten([1,2,[3, 4, [5,6,[7]]]], undefined, false); // [1, 2, 3, 4, 5, 6, 7]
参考资料: JavaScript专题之数组扁平化