常见的数组技巧
前端开发的过程中,常常需要我们操作数组来获取想要的数据,今天就总结一些在 JS 开发中我们需要常用到的方法。
去重
数组去重的方法大家都知道几个,我现在想从优化耗时的角度去思考。
创建测试模板
使用 time 和 timeEnd 来检测测试函数运行时间,就可以了解各个方法的性能。
// 创建两个比较大的数据的数组let arr1 = Array.from(new Array(100000), (x, index) => index);let arr2 = Array.from(new Array(50000), (x, index) => index + index);// 定义一个测试方法const test = function() {};// 计时console.time();// 执行函数test();// 结束console.timeEnd();
开始
使用双重for循环
解释:外层遍历每一个元素,内循环比较一遍,有相同的使用push或者splice
function test(a, b) {const arr = a.concat(b);for (let i = 0; i < arr.length; i++) {for (let j = i + 1; j < arr.length; j++) {if (arr[i] === arr[j]) {arr.splice(j, 1);// splice 改变数组长度arr.length--;j--;}}}}
default: 11152.2509765625ms
使用filter和indexOf
解释:这个实现比较简单,利用filter返回条件为真的数组元素,indexOf会返回检索的字符在整个数组中的位置,正好和数组的下标对应。
function test(a, b) {const arr = a.concat(b);return arr.filter((item, index) => {return arr.indexOf(item) === index;});}
default: 10116.416015625ms
使用indexOf和for循环
解释: 返回数组中某个指定的元素的位置,没有返回则返回-1.
function test(a, b) {const arr = a.concat(b);for (let i = 0; i < arr.length; i++) {if (arr.index(arr[i]) === -1) {arr.push(arr[i]);}}return arr;}
default: 9876.494873046875ms
使用数组方法includes
解释:传入指定元素,如果存在返回true,没有则返回false。
function test(a, b) {const arr = a.concat(b);let arr1 = [];for (let i = 0; i < arr.length; i++) {if (!arr1.includes(arr[i])) {arr1.push(arr[i]);}}return arr1;}
default: 9848.931884765625ms
使用sort方法
解释:使用相邻元素比较的方法
function test(a, b) {let arr = a.concat(b);arr = arr.sort();const arr1 = [arr[0]];for (let i = 0; i < arr.length; i++) {if (arr[i] !== arr[i - 1]) {arr1.push(arr[i]);}}return arr1;}
default: 26.5078125ms
使用 Map
解释:创建一个空 Map 的数据结构,遍历需要去重的数组,把数组的每一个元素作为 key 存入 map 中。由于 map 不会出现相同的 key 实现去重,最后得到去重的结果。
function test(a, b) {let arr = a.concat(b);let map = new Map();let arr1 = [];for (let i = 0; i < arr.length; i++) {if (map.has(arr[i])) {map.set(arr[i], true);} else {map.set(arr[i], false);arr1.push(arr[i]);}}return arr1;}
default: 28.06005859375ms
使用 Set 和展开运算符…
解释:Set 为 ES6 新增的一个对象,允许存储任何类型(原始值或引用值)的唯一值.
function test(a, b) {let arr = a.concat(b);// 一const arr1 = Array.from(new Set(arr));// 二// const arr1 = [...new Set(arr)]console.log(arr1);}
default: 18.821044921875ms
使用 for...of 和 Object 的属性不会重复的特点
解释: ES6 中引入的 for…of 循环,以替代 for…in 和 forEach() ,并支持新的迭代协议。for…of 允许你遍历 Arrays(数组), Strings(字符串), Maps(映射), Sets(集合)等可迭代的数据结构等。
function test(a, b) {const arr = a.concat(b);const arr1 = [];const obj = {};for (let i of arr) {if (!obj[i]) {arr1.push(i);obj[i] = 1;}}return arr1;}
default: 19.759033203125ms
小节
其实去重的方法还有很多,毕竟个人经历有限就不全部列举出来了,在这里我们就能够看出来,谁的执行时间比较短,总结来说还是使用 Set 最为方便。
splice 实现增删改
array.splice(start[,deleteCount[,item1[,item2[,…]]]]) 详情请参照
start
指定修改的开始位置(从0计数)。如果超出了数组的长度,则从数组末尾开始添加内容;如果是负值,则表示从数组末位开始的第几位(从-1计数,这意味着-n 是倒数第 n 个元素并且等价于array.length-n);如果负数的绝对值大于数组的长度,则表示开始位置为第0位。
deleteCount 可选
整数,表示要移除的数组元素的个数。
如果 deleteCount 大于 start 之后的元素的总数,则从 start 后面的元素都将被删除(含第 start 位)。
如果 deleteCount 被省略了,或者它的值大于等于 array.length - start(也就是说,如果它大于或者等于start之后的所有元素的数量),那么start之后数组的所有元素都会被删除。
如果 deleteCount 是 0 或者负数,则不移除元素。这种情况下,至少应添加一个新元素。
item1, item2, ... 可选
要添加进数组的元素,从 start 位置开始。如果不指定,则 splice() 将只删除数组元素。
splice 的删除
const arr = [1, 2, 3, 4];/* start为正数 */// 1.如果不传递要移除的数组元素的参数, 默认删除开始位置(包括)后面所有的元素const removed = arr.splice(1);console.log(arr); // [2]console.log(removed); // [1, 3, 4]// 2.传递参数,表示要删除的个数const removed = arr.splice(1, 1);console.log(arr); // [1, 3, 4]console.log(removed); // [2]/* start为负数,数组将从后往前 */// 1.如果不传递要移除的元素的参数,且传入的start为负数,则负号表示的含义就是:从倒数第几个参数开始,后面的全部删除const removed = arr.splice(-2);console.log(arr); // [1, 2]console.log(removed); // [3,4]
splice 的添加
// 添加其实很好理解,就是删除0个元素const arr = [1, 2, 3, 4];// 在数组下标为1的位置删除0个元素,并插入一个新的元素 5const removed = arr.splice(1, 0, 5);console.log(arr); // [1,5,2,3,4]console.log(removed); // []
splice 的修改
// 修改就是删除元素的同时再添加新的元素const arr = [1, 2, 3, 4];// 在数组下标为1 的地方删除1个元素,同时插入一个元素const removed = arr.splice(1, 1, [5]);console.log(arr); // [1, [5], 3, 4]console.log(removed); // [2]
splice 的进阶
我们可以通过上面的方法进行一个简单的总结:
- 我们可以使用
splice模拟数组的其他方法push: splice(arr.length, 0, x)
unshift: splice(0, 0, x)
shift: splice(0, 1)
pop: splice(arr.length-1, 1)
生成随机数
生成 x 到 y 之间的随机数
function random(x, y) {// 这样不需要判断x和y的大小return Math.floor(y - Math.random() * (y - x));}
拓展:生成随机颜色
function random(x) {return Math.floor(Math.random() * x);}function rgb() {//rgb颜色随机const r = random(256);const g = random(256);const b = random(256);const rgb = "(" + r + "," + g + "," + b + ")";// 转换成16进制// const color = '#' + r.toString(16) + g.toString(16) + b.toString(16)return rgb;}
filter
方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
- 去除假值
function boo(item) {if (item) {return true;}}const arr = ["", 0, false, NaN, undefined, true, {}];const filterArr = arr.filter(boo);// 等价于下面这种方法// const filterArr = arr.filter(Boolean)
- 配合
indexOf或者include进行去重
const arr = [1, 1, 1, 2, 3, 4, 2, 4];const filterArr = arr.filter((item, index, arr) => arr.indexOf(item) === index);
当然我在上面也说了这个方法在进行较大数据的时候,性能很低,请酌情使用。
- 求数组中的交集
const arr1 = [1, 2, 3, 4, 5];const arr2 = [2, 3, 4];const arr = arr2.filter(item => arr1.indexOf(item) !== -1);// const arr = arr1.filter(item => arr2.includes(item))
reduce
这个方法很重要也很实用,其实列举起来有很多的高级功能和方法。
本来也打算做一个比较好的总结,但是大家可以看下面的会更好!
详情请参照 25 个你不得不知道的数组 reduce 高级用法(必备硬核骚技能)
