常见的数组技巧
前端开发的过程中,常常需要我们操作数组来获取想要的数据,今天就总结一些在 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个元素,并插入一个新的元素 5
const 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 高级用法(必备硬核骚技能)