除了flat可以让数组扁平化之外还有其他方法进行扁平化吗?

251 阅读2分钟

如何对数组进行降维?

将数组扁平化其实就是给数组降维,flat()这个API这一实现数组降维 ,可以对flat进行传参,参数表示要降的维数。

image.png

在这里我们主要讨论的是:是否可以不用flat这个API而对数组进行扁平化呢?

答:当然能了!

方法一:使用 concat()

concat() 方法用于合并多个数组,是一个纯函数,因为此方法不会改变原数组,而是返回一个新数组。

先来看一个简单的例子吧:

var arr1 = [1,2,3];
var arr2 = [4,5]; 
console.log(arr1.concat(arr2)); // [1, 2, 3, 4, 5]

由上面例子可以看出,concat()是可以合并多个数组的。那么,如果是一个多维数组呢?还可以合并吗?当然能了呀,但是右下图可见此数组最后一个元素是一个数组,那么今天的重点来了!

image.png

知道了concat是干什么的之后,就要考虑用concat 怎样才能实现扁平化呢?

主要思路如下:

首先封装一个扁平化的函数 function flatten(){ }; 在函数里遍历整个数组 在flatten()函数里定义一个新数组用于接受扁平化的新数组 在flatten()函数里还需要定义一个函数去判断遍历元素是否是一个数组,若是遍历元素1一个数组,则需要在此调用flatten(),若判断遍历元素1里面还有数组,那么继续调用flatten()…知道没有数组位置,这个过程就是所谓的递归;若遍历元素1是一个正常的字符串则将次元素存到nArr这个新数组里面。

    var arr = [[12, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14]]]]];
    // 封装flatten()函数
    function flatten(array) {
        // 创建一个新数组
        let nArr = [];
        // 定义函数判断是否是数组
        function isArr(isarr) {
            // {}.toString.call(isarr)调用了根元素的
            return {}.toString.call(isarr) == '[object Array]';
        }
        for (var i = 0; i < array.length; i++) {
            if (isArr(array[i])) {
                nArr = nArr.concat(flatten(array[i]))
            } else {
                nArr.push(array[i])
            }
        }
    }
    console.log(flatten(arr)); // [12, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14]
    
注: {}.toString.call( ) 检测数据的原型
 	“根”原型(Object.prototype)下,有个toString的方法,记录着所有 数据类型(构造函数)
	.call作用是改this指向。让传入的对象,执行 “根”原型的toString方法

也可以在Array.prototype上添加一个myflatten属性

    var arr = [[12, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14]]]]];
    Array.prototype.myflatten = function(){
        let _arr = this;
        let nArr = [];
        let toStr = {}.toString;

        _arr.forEach(function(item){
            toStr.call(item) === '[object Array]' 
                ? nArr = nArr.concat(item.myflatten()) 
                : nArr.push(item)
        })

        return nArr;
    }
    console.log(arr.myflatten()); // [12, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14]

方法二:reduce()

**array.reduce(function(total, currentValue, currentIndex, arr), initialValue) **

第一个参数是一个函数,每一次运行返回的结果会将作为参数传入;第二个参数是传递给函数的初始值,会赋给total。

举个例子具体感受以下reduce()的作用吧!

    let arr=[1,2,5,9,12,13,15];
    arr.reduce(function(prev,next){
        console.log("prev:",prev,"next:",next);
        return prev+next;
    },100)

结果如下:
prev: 100 next: 1
prev: 101 next: 2
prev: 103 next: 5
prev: 108 next: 9
prev: 117 next: 12
prev: 129 next: 13
prev: 142 next: 15

首先prev的初值就是100,next的初值为1。运行一次,所返回的值就是下一次prev的值,而next的值则是依次遍历数组的值。所以第一次运行时 prev:100;next:1;第二次运行时 prev:100+1=101;next:2; 第三次运行时 prev:101+2=103;next:5;以此类推…最终得出结果。

我们可以利用reduce这个特点进行对数组的扁平化,在Array.prototype上添加一个myflatten属性,,myflatten属性返回一个由于调用reduce这个API而得到的一个扁平化数组,给reduce的第二个参数初始值赋一个空数组,reduce第一个参数的形参prev与next进行拼接,拼接前需要判断next是否是一个数组,如果next是一个数组则需要调用此数组的myflatten属性进行扁平化,继续执行判断(也就是递归),如果next不是数组则直接与prev拼接,最后返回prev与next 的拼接数组 代码如下:

    var arr = [[12, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14]]]]];
    Array.prototype.myflatten = function () {
        let _arr = this;
        let toStr = {}.toString;
        return _arr.reduce(function (prev, next) {
            return prev.concat(toStr.call(next) === "[object Array]" ? next.myflatten() : next)
        }, [])
    }
    console.log(arr.myflatten()); // [12, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14]

也可以封装成一个扁平化的函数:

    var arr = [[12, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14]]]]];
    let myflatten=(arr)=> {
        return arr.reduce((prev, next)=>{
            return prev.concat({}.toString.call(next) === "[object Array]" ? myflatten(next) : next)
        }, [])
    }
    console.log(myflatten(arr)); // [12, 2, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 12, 13, 14]