问题描述
JS 两个数组,如代码所示。为什么b数组内的1没有全部删除呢?求大神指点
相关代码
var a = [1];
var b = [1, 1, 1, 2, 5, 8]
a.forEach((item) => {
b.forEach((val,index) => {
if (val == item) {
b.splice(index, 1);
}
});
});
console.log(b); // [ 1, 2, 5, 8 ]
JS 两个数组,如代码所示。为什么b数组内的1没有全部删除呢?求大神指点
var a = [1];
var b = [1, 1, 1, 2, 5, 8]
a.forEach((item) => {
b.forEach((val,index) => {
if (val == item) {
b.splice(index, 1);
}
});
});
console.log(b); // [ 1, 2, 5, 8 ]
第一遍 index === 0
var a = [1];
var b = [1(被删除), 1, 1, 2, 5, 8]
第二遍 index === 1
var a = [1];
var b = [1, 1(被删除), 2, 5, 8]
第三遍 index === 2
var a = [1];
var b = [1, 2, 5(比较这个), 8]
第三遍 index === 3
var a = [1];
var b = [1, 2, 5, 8(比较这个)]
结果 [1, 2, 5, 8]
不能使用forEach,使用splice删除元素后,索引index的值仍然是累加的。会使一些元素未遍历到。
a.forEach((item) => {
for (var i = 0; i < b.length; i++) {
if (item === b[i]) {
b.splice(i, 1)
i--
}
}
});
不应该在循环中增删数组元素。
因为会导致删除元素后的元素直接补到当前已删除元素的位置,占用当前下标,而下一次遍历的时候,下标+1自然而然跳过了这个元素。
逆序遍历可以解决这个问题,更稳妥的办法是使用filter。
找出b中不在a中的元素, 写法一:
const result = b.filter(i => !a.includes(i))
写法二、
const map = a.reduce((r, i) => (r[i] = 1, r), {})
const result = b.filter(i => !map[i])
写法二比写法一要优,写法一时间复杂度O(m x n),写法二时间复杂度O(m + n)。
var a = [1];
var b = [1, 1, 1, 2, 5, 8];
var result= b.reduce((_result_,b_item)=>{
if(a.indexOf(b_item)===-1){
_result_.push(b_item);
}
return _result_;
},[]);
console.log('b after clear:',result);
8 回答4.8k 阅读✓ 已解决
6 回答3.5k 阅读✓ 已解决
5 回答2.9k 阅读✓ 已解决
5 回答6.4k 阅读✓ 已解决
4 回答2.3k 阅读✓ 已解决
4 回答2.8k 阅读✓ 已解决
3 回答2.5k 阅读✓ 已解决
调用
splice
操作元素时,未被操作的元素会自动移位,不会出现undefined
。forEach
中val
对应的index
是刚开始调用forEach
时的index
。以为例,在
forEach
的第一次循环里,数组第一个元素被删除后,数组变为[1.2, 1.3, 2, 5, 8]
,第二次循环中val = 1.2, index = 2
,满足条件,删去第二个元素,相当于原数组第三个元素,变为[1.2, 2, 5, 8]
。要解决这个问题,可以像 qinchao888 那样用
for
,删去后i--
更正i
值,也可以用更加语义化的 ES6 的filter
:或者这样:(推荐)
qinchao888 的方法不会创建新数组,保留了原数组的内存引用,性能上似乎也更好。这里给出的两种 ES6 的方法都会创建新数组,再重新给
b
赋值,但语义比较清晰。这两种方法中,第一种可能会在内存中创建多个数组,a
有几个元素创几个;第二种方法既简单又只会在内存中创建一个新数组,比较推荐。Array.prototype.splice() | MDN
Array.prototype.forEach() | MDN
Array.prototype.filter() | MDN
Array.prototype.includes() | MDN