JS数组扁平化

975 阅读3分钟

扁平化

将一个嵌套多层的数组 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))
}