JS数组扁平化的思路与实现

2,018 阅读3分钟

其实最核心的思路就是递归,如果单纯用编程语言实现,而不是使用一些巧妙的API的话,本质的思路就是递归,因为JS数组多层嵌套,实现扁平化的过程其实就是层层递归取出元素的过程,这里给出三种实现JS数组扁平化的方法。

  • 纯递归实现
  • 扩展运算符
  • 降维打击

纯递归实现

算法的步骤是这样的:

  1. 顺序扫描初始数组{arr}的第一维,取元素element,若element还是数组则执行3,若element是单个元素则执行2
  2. 将单元素加入newArr的一维数组中
  3. 把{element}数组作为新的初始数组,执行1

转化为JS代码,可以这样写:

function(arr,newArr){
        arr.forEach(element => {
           if(!Array.isArray(element)){
                newArr.push(element);
           }else{
                return arguments.callee(element,newArr);
           } 
        });
        return newArr;
    }

扩展运算符

首先要了解ES6中的扩展运算符... , 三点运算符可以将二维数组直接扁平化

算法的步骤是这样的:

  1. 判断初始数组{arr}是否至少是二维数组,也就是说arr数组中是否包含数组项,若包含则执行2,若不包含则执行3
  2. 使用扩展运算符对数组扁平化,并将扁平化后数组{arr}置为初始数组,然后以此为参数执行1
  3. 已经扁平化为一维数组了,直接返回结果

转化为JS代码,可以这样写:

function(arr){
        if(!arr.some(element => {
            return Array.isArray(element);
        })){
            return arr;
        }else{
            arr = [].concat(...arr);
            return arguments.callee(arr);
        }
    }

降维打击

这是巧妙使用JS的APItoString方法来实现的,调用Array.prototype.toString,会返回将数组中的元素使用,连接起来的字符串

思路很简单,就是数组被toString方法降维后的字符串使用split方法还原为一维数组。 但是这里需要处理一个问题,就是如果数组中的元素是数字而不是字符或字符串,那么split切割还原后的字符或字符串需要转换为数字。这里我使用||运算符做了一个巧妙的适配。

function(arr){
        return arr.toString().split(',').map(function(val){
            return parseInt(val) || parseFloat(val) || val;
        });
    }

这段代码是不是很简洁,这也是我喜欢JS这门语言的原因,可以写出一些超级简洁干净的代码,赏心悦目😁

最后,我们重构一下代码

最近在看设计模式,这里我们使用不同的算法和表达实现了同一种能力,但是有些函数的参数不尽相同,为了易于维护,我们不妨写一个代理执行的函数,来屏蔽掉原函数的异构型。


    //最普通的方式,递归实现,遇到是数组的元素就递归
    function common(arr,newArr){
        arr.forEach(element => {
           if(!Array.isArray(element)){
                newArr.push(element);
           }else{
                return arguments.callee(element,newArr);
           } 
        });
        return newArr;
    }
    //降维打击 : 利用了toString方法的特性,或者是join方法也可以
    function reduceDimen(arr){
        return arr.toString().split(',').map(function(val){
            return parseInt(val) || parseFloat(val) || val;
        });
    }
    //扩展运算符 和 递归 结合使用
    function extendOpt(arr){
        if(!arr.some(element => {
            return Array.isArray(element);
        })){
            return arr;
        }else{
            arr = [].concat(...arr);
            return arguments.callee(arr);
        }
    }
//代理执行,屏蔽掉不同函数的参数异构,又使得算法表达可配置
var flatten = function(func,arr) {
    if(arguments.length === 1){
        console.log(common(func,[]));
        return;
    }
    if(func === common){
        console.log(func(arr,[]));
        return ;
    }
    console.log(func(arr));
}
//测试
var arr = [
    [1,2,[
        10,34,22
    ]],
    [[
        2,3,58
    ],2,5],
    [6,4,9]
];
flatten(arr);
flatten(common,arr);
flatten(reduceDimen,arr);
flatten(extendOpt,arr);
//结果均输出 : [ 1, 2, 10, 34, 22, 2, 3, 58, 2, 5, 6, 4, 9 ]