VIP用户交流群:462197261 收藏本站北冥有鱼 互联网前沿资源第一站 助力全行业互联网+
在线客服:78895949
tonglan
  • 当前位置:
  • JavaScript之数组扁平化详解

    扁平化

    数组的扁平化,就是将一个嵌套多层的数组 array (嵌套可以是任何层数)转换为只有一层的数组。

    举个例子,假设有个名为 flatten 的函数可以做到数组扁平化,效果就会如下:

    var arr = [1, [2, [3, 4]]];
    console.log(flatten(arr)) // [1, 2, 3, 4]

    知道了效果是什么样的了,我们可以去尝试着写这个 flatten 函数了

    递归

    我们最一开始能想到的莫过于循环数组元素,如果还是一个数组,就递归调用该方法:

    // 方法 1
    var arr = [1, [2, [3, 4]]];
    function flatten(arr) {
    var result = [];
    for (var i = 0, len = arr.length; i < len; i++) {
    if (Array.isArray(arr[i])) {
    result = result.concat(flatten(arr[i]))
    }
    else {
    result.push(arr[i])
    }
    }
    return result;
    }
    console.log(flatten(arr))
    

    toString

    如果数组的元素都是数字,那么我们可以考虑使用 toString 方法,因为:

    [1, [2, [3, 4]]].toString() // "1,2,3,4"

    调用 toString 方法,返回了一个逗号分隔的扁平的字符串,这时候我们再 split,然后转成数字不就可以实现扁平化了吗?

    // 方法2
    var arr = [1, [2, [3, 4]]];
    function flatten(arr) {
    return arr.toString().split(',').map(function(item){
    return +item
    })
    }
    console.log(flatten(arr))
    
    

    然而这种方法使用的场景却非常有限,如果数组是 [1, '1', 2, '2'] 的话,这种方法就会产生错误的结果。

    reduce

    既然是对数组进行处理,最终返回一个值,我们就可以考虑使用 reduce 来简化代码:

    // 方法3
    var arr = [1, [2, [3, 4]]];
    function flatten(arr) {
    return arr.reduce(function(prev, next){
    return prev.concat(Array.isArray(next) ? flatten(next) : next)
    }, [])
    }
    console.log(flatten(arr))
    

    ES6 增加了扩展运算符,用于取出参数对象的所有可遍历属性,拷贝到当前对象之中:

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

    我们用这种方法只可以扁平一层,但是顺着这个方法一直思考,我们可以写出这样的方法:

    // 方法4
    var arr = [1, [2, [3, 4]]];
    function flatten(arr) {
    while (arr.some(item => Array.isArray(item))) {
    arr = [].concat(...arr);
    }
    return arr;
    }
    console.log(flatten(arr))
    

    undercore

    那么如何写一个抽象的扁平函数,来方便我们的开发呢,所有又到了我们抄袭 underscore 的时候了~

    在这里直接给出源码和注释,但是要注意,这里的 flatten 函数并不是最终的 _.flatten,为了方便多个 API 进行调用,这里对扁平进行了更多的配置。

    /**
    * 数组扁平化
    * @param {Array} input 要处理的数组
    * @param {boolean} shallow 是否只扁平一层
    * @param {boolean} strict 是否严格处理元素,下面有解释
    * @param {Array} output 这是为了方便递归而传递的参数
    * 源码地址:https://github.com/jashkenas/underscore/blob/master/underscore.js#L528
    */
    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, len = value.length;
    while (j < len) 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,在代码里我们可以看出,当遍历数组元素时,如果元素不是数组,就会对 strict 取反的结果进行判断,如果设置 strict 为 true,就会跳过不进行任何处理,这意味着可以过滤非数组的元素,举个例子:

    var arr = [1, 2, [3, 4]];
    console.log(flatten(arr, true, true)); // [3, 4]

    那么设置 strict 到底有什么用呢?不急,我们先看下 shallow 和 strct 各种值对应的结果:

    • shallow true + strict false :正常扁平一层
    • shallow false + strict false :正常扁平所有层
    • shallow true + strict true :去掉非数组元素
    • shallow false + strict true : 返回一个[]

    我们看看 underscore 中哪些方法调用了 flatten 这个基本函数:

    _.flatten

    首先就是 _.flatten:

    _.flatten = function(array, shallow) {
    return flatten(array, shallow, false);
    };

    在正常的扁平中,我们并不需要去掉非数组元素。

    _.union

    接下来是 _.union:

    该函数传入多个数组,然后返回传入的数组的并集,

    举个例子:

    _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
    => [1, 2, 3, 101, 10]
    

    如果传入的参数并不是数组,就会将该参数跳过:

    _.union([1, 2, 3], [101, 2, 1, 10], 4, 5);
    => [1, 2, 3, 101, 10]
    

    为了实现这个效果,我们可以将传入的所有数组扁平化,然后去重,因为只能传入数组,这时候我们直接设置strict 为 true,就可以跳过传入的非数组的元素。

    // 关于 unique 可以查看《JavaScript专题之数组去重》[](https://github.com/mqyqingfeng/Blog/issues/27)
    function unique(array) {
    return Array.from(new Set(array));
    }
    _.union = function() {
    return unique(flatten(arguments, true, true));
    }
    

    _.difference

    是不是感觉折腾 strict 有点用处了,我们再看一个 _.difference:

    语法为:

    _.difference(array, *others)

    效果是取出来自 array 数组,并且不存在于多个 other 数组的元素。跟 _.union 一样,都会排除掉不是数组的元素。

    举个例子:

    _.difference([1, 2, 3, 4, 5], [5, 2, 10], [4], 3);
    => [1, 3]
    

    实现方法也很简单,扁平 others 的数组,筛选出 array 中不在扁平化数组中的值:

    function difference(array, ...rest) {
    rest = flatten(rest, true, true);
    return array.filter(function(item){
    return rest.indexOf(item) === -1;
    })
    }
    

    注意,以上实现的细节并不是完全按照 underscore,具体细节的实现感兴趣可以查看源码。

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持北冥有鱼。

    您可能感兴趣的文章:

    • JS数组扁平化(flat)方法总结详解
    • js嵌套的数组扁平化:将多维数组变成一维数组以及push()与concat()区别的讲解
    • 用javascript实现改变TEXTAREA滚动条和按钮的颜色,以及怎样让滚动条变得扁平
    • js获取对象,数组所有属性键值(key)和对应值(value)的方法示例
    • javascript实现的字符串转换成数组操作示例
    • JS扁平化输出数组的2种方法解析

    广而告之:
    热门推荐:
    支付宝支付开发——当面付条码支付和扫码支付实例

    本文介绍支付宝中当面付下属的条码支付、扫码支付、订单查询、退款申请的集成开发过程。  本文分为以下五个部分: 条码支付和扫码支付介绍 申请应用 密钥生成及配置 API及SDK集成 条码支付、扫码支付、订单查询、退款申请  一、条码支付及二维码支付介绍 1. 条码支···

    帝国cms6.6版本新增专题属性自定义字段功能,让专题更灵活出色

    帝国CMS 6.6版新增专题属性自定义字段功能,可以在各式专题增加相应的特殊属性,让专题更灵活出色。 专题自定义字段功能介绍: (1)、支持自定义专题字段,字段包括支持text/textarea/编辑器/单选/多选等。 (2)、内置调用专题自定义字段函数:ReturnZtAddField(专题ID,字段名),···

    基于HTML5 Canvas的3D动态Chart图表的示例

    发现现在工业SCADA上或者电信网管方面用图表的特别多,虽然绝大部分人在图表制作方面用的是echarts,他确实好用,但是有些时候我们不能调用别的插件,这个时候就得自己写这些美丽的图表了,然而图表轻易做不成美丽的。。。看到有一个网站上在卖的图表,感觉挺好看的,就用 HT f···

    PHP mysqli事务操作常用方法分析

    本文实例讲述了PHP mysqli事务操作常用方法。分享给大家供大家参考,具体如下: 1、 //打开(true)或关闭(false)本次数据库连接的自动命令提交事务模式 //参数如果设置为 FALSE,则表示关闭 auto-commit。如果设置为 TRUE,则表示开启 auto-commit(提交任何等待查询)。 bool ···

    vue使用jsonp抓取qq音乐数据的方法

    1、安装jsonp npm install jsonp 2、创建jsonp.js文件,内容如下: import originJSONP from 'jsonp' /** * 封装jsonp * @param {*} url 原始的jsonp第一个参数是url,第二个参数是option,这里为了比较好写参数做了下封装 * @param {obj} data 参数 * @param {*} option j···

    批量修改RAR文件注释的php代码

    我们打开WINRAR的帮助文件,帮助文件中提到了在命令行模式下修改RAR文件注释及添加压缩文档的两个参数分别为A\C,WINRAR的说明文件如下: 从当前文件夹添加全部 *.hlp 文件到压缩文件 help.rar 中 WinRAR a help *.hlp 从文件添加注释可以使用 -z<文件> 开关。 WinR···

    详解用webpack2搭建angular2的项目

    webpack2和angular2搭建的项目 github地址:项目链接 npm install,安装依赖包 npm run dev,启动本地工程,在localhost:1699进行预览 package.json { "name": "angular-webpack", "version": "1.0.0", "description": "webpack2 & angular2", "scripts": { "dev":···

    随机调用非终极栏目下栏目信息

    SQl语句:select * from phome_ecms_article where checked=1 and ".ReturnClass($class_r[$GLOBALS[navclassid]][sonclass])." order by rand() desc limit 6

    vue2手机APP项目添加开屏广告或者闪屏广告

    一般项目里,有的会在启动的时候加开屏广告或者闪屏广告。我们是在index.html通过定位来做的。如下: <style media="screen"> #entry { width: 100%; height: 100%; z-index: 200; position: relative; } #entryAdv { display: none; } #entryTim { positio···

    PHP常见过waf webshell以及最简单的检测方法

    前言 之前在Webshell查杀的新思路中留了一个坑 ️,当时没有找到具体找到全部变量的方法,后来通过学习找到了个打印全部量的方法,并再次学习了下PHP webshell绕过WAF的方法,以此来验证下此方法是否合理。 如有错误,还请指出,不胜感激! :turtle:拜 在那篇文章中我突···