JavaScript之数组扁平化详解


Posted in Javascript onJune 03, 2019

扁平化

数组的扁平化,就是将一个嵌套多层的数组 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,具体细节的实现感兴趣可以查看源码。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JS 实现完美include载入实现代码
Aug 05 Javascript
JQuery以JSON方式提交数据到服务端示例代码
May 05 Javascript
原生JavaScript制作微博发布面板效果
Mar 11 Javascript
滚动条的监听与内容随着滚动条动态加载的实现
Feb 08 Javascript
jquery实现表单获取短信验证码代码
Mar 13 Javascript
详解微信小程序 相对定位和绝对定位
May 11 Javascript
JS简单实现数组去重的方法分析
Oct 14 Javascript
three.js中文文档学习之创建场景
Nov 20 Javascript
JS中利用FileReader实现上传图片前本地预览功能
Mar 02 Javascript
vue2中使用less简易教程
Mar 27 Javascript
JS实现中英文混合文字溢出友好截取功能
Aug 06 Javascript
uni-app自定义导航栏按钮|uniapp仿微信顶部导航条功能
Nov 12 Javascript
JavaScript从原型到原型链深入理解
Jun 03 #Javascript
生产制造追溯系统之再说条码打印
Jun 03 #Javascript
VUE安装使用教程详解
Jun 03 #Javascript
教你完全理解ReentrantLock重入锁
Jun 03 #Javascript
生产制造追溯系统之在线打印功能
Jun 03 #Javascript
产制造追溯系统之通过微信小程序实现移动端报表平台
Jun 03 #Javascript
深入理解 JS 垃圾回收
Jun 03 #Javascript
You might like
php下实现一个阿拉伯数字转中文数字的函数
2008/07/10 PHP
如何在HTML 中嵌入 PHP 代码
2015/05/13 PHP
PHP+Mysql+jQuery文件下载次数统计实例讲解
2015/10/10 PHP
js与jquery实时监听输入框值的oninput与onpropertychange方法
2015/02/05 Javascript
基于jQuery实现仿百度首页换肤背景图片切换代码
2015/08/25 Javascript
全面解析jQuery $(document).ready()和JavaScript onload事件
2016/06/08 Javascript
微信小程序 跳转传参数与传对象详解及实例代码
2017/03/14 Javascript
JS实现自动轮播图效果(自适应屏幕宽度+手机触屏滑动)
2017/06/19 Javascript
js实现音乐播放控制条
2017/09/09 Javascript
使用vue-cli(vue脚手架)快速搭建项目的方法
2018/05/21 Javascript
JS 中可以提升幸福度的小技巧(可以识别更多另类写法)
2018/07/28 Javascript
微信小程序dom操作的替代思路实例分析
2018/12/06 Javascript
nodejs同步调用获取mysql数据时遇到的大坑
2019/03/02 NodeJs
VUE路由动态加载实例代码讲解
2019/08/26 Javascript
Vue.js数字输入框组件使用方法详解
2019/10/19 Javascript
高效jQuery选择器的5个技巧实例分析
2019/11/26 jQuery
JS 5种遍历对象的方式
2020/06/16 Javascript
[41:54]2018DOTA2亚洲邀请赛 4.1 小组赛A组加赛 TNC vs Liquid
2018/04/03 DOTA
[01:08:44]NB vs VP 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
Python实现基本数据结构中栈的操作示例
2017/12/04 Python
python 把列表转化为字符串的方法
2018/10/23 Python
Python实现FTP弱口令扫描器的方法示例
2019/01/31 Python
Python适配器模式代码实现解析
2019/08/02 Python
基于python实现自动化办公学习笔记(CSV、word、Excel、PPT)
2019/08/06 Python
html5简单示例_动力节点Java学院整理
2017/07/07 HTML / CSS
前端实现背景虚化但内容清晰且自适应 的实例代码
2019/08/01 HTML / CSS
美国女性奢华品牌精品店:INTERMIX
2017/10/12 全球购物
C++:局部变量能否和全局变量重名
2014/03/03 面试题
Java面试题:说出如下代码的执行结果
2015/10/30 面试题
中层干部岗位职责
2013/12/18 职场文书
2014道德模范事迹材料
2014/02/16 职场文书
建筑施工安全责任书
2014/07/24 职场文书
2015年领导干部廉洁自律工作总结
2015/05/26 职场文书
2019年图书室自查报告范本
2019/10/12 职场文书
导游词之井冈山
2019/11/20 职场文书
mybatis调用sqlserver存储过程返回结果集的方法
2021/05/08 SQL Server