扁平化
将一个嵌套多层的数组 array (数组)(嵌套可以是任何层数)转换为只有一层的数组。
例如:
//无论嵌套的层数为多少层
[[10, 20, [50, 100]], 50] => [10, 20, 50, 100, 50]
递归
很显然,只要循环数组元素,再依次判断数组的每一个元素是否是数组,若是数组的话,则递归调用该方法即可。
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
}
flatten([[10, 20, [50, 100]], 50]) // [10, 20, 50, 100, 50]
我们可以将数组最外层的循环使用数组的 reduce 方法来简化。
Array.prototype.reduce
reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
function flatten(arr) {
return arr.reduce((accumulator, currentValue) => {
return accumulator.concat(Array.isArray(currentValue) ? flatten(currentValue) : currentValue)
}, [])
}
console.log(flatten([[10, 20, [50, 100]], 50])) // [10, 20, 50, 100, 50]
_.flatten
可以查看 underscore.js 中的扁平化函数是如何实现的,下列是实现方法。
/**
* 数组扁平化
* @param {Array} input 要处理的数组
* @param {boolean} shallow 是否只扁平一层
* @param {boolean} strict 是否严格处理元素,该值为 true 时忽略输入数组中的非数组元素
* @param {Array} output 这是为了方便递归而传递的参数
*/
function flatten(input, shallow, strict, output) {
// 递归的时候会用到output
output = output || [];
var idx = output.length;
for (var i = 0, len = input.length; i < len; i++) {
var value = input[i];
if (Array.isArray(value)) {
// 如果只扁平一层,遍历该数组,依此填入output
if (shallow) {
var j = 0, length = value.length;
while (j < length) output[idx++] = value[j++];
}
// 如果是全部扁平就递归,传入已经处理的 output,递归中接着处理 output
else {
flatten(value, shallow, strict, output);
idx = output.length;
}
}
// 不是数组,根据 strict 的值判断是跳过不处理还是放入到 output 中
else if (!strict){
output[idx++] = value;
}
}
return output;
}
//当strict为true时则对数组中的非数组元素不会进行处理
//下列输入数组中会跳过元素10 和20
flatten([10,20,[30,40,50]], true,true) // [30,40,50]
//当shallow为true时,表示只扁平一层
flatten([10,20,[30,40,50,[100]]], true) //[10,20,30,40,50,[100]]
可以看 到underscore.js 中的扁平化函数为:
_.flatten = function(array, shallow) {
return flatten(array, shallow, false);
}
同时 underscore.js 中的 _.union方法(用于返回多个传入数组的并集) 也使用了这个扁平化方法。
//如果传入的参数并不是数组,就会将该参数跳过
_.union([1, 2, 3], [101, 2, 1, 10], [2, 1],100,200)
=> [1, 2, 3, 101, 10]
_.union方法可以这样实现,先将传入的所有数组进行扁平化处理,然后使用数组去重方法 _.uniq 将重复的元素去掉,即可得到所有输入数组的并集。
_.union = function() {
// 先浅度严格一维化,再去重,得到并集
return _.uniq(flatten(arguments, true, true))
}