Js数组枚举过程中删除元素
工作中需要一边枚举, 一边删除元素. 然后, 悲剧了.
const log = console.log
const a=[1,2,3,4,5]
for (const key in a) {
log("a[key]: ",a[key]) //f {}
a.splice(key, 1)
}
log("a: ",a) //f {}
这一段的输出是:
a[key]: 1
a[key]: 3
a[key]: 5
a: [ 2, 4 ]
//本身我盼望的输出是:
a[key]: 1
a[key]: 2
a[key]: 3
a[key]: 4
a[key]: 5
a: [ ]
这篇文章会对程序员形成严重的误导: https://love2dev.com/blog/javascript-remove-from-array/
//一段忽悠人的样例代码
var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
for( var i = 0; i < array.length-1; i++){ //这个length-1也是错误写法, 会漏掉最后一个元素
if ( array[i] === 5) {
array.splice(i, 1);
}
}
log("array: ",array)
//=> [1, 2, 3, 4, 6, 7, 8, 9, 0]
//真相大白
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
for( var i = 0; i < arr.length-1; i++){
if ( arr[i] === 5||arr[i]===6) { //这里再加一个条件就真相了.
arr.splice(i, 1);
}
}
log("arr: ",arr)
//=> [1, 2, 3, 4, 6, 7, 8, 9, 0] 看到没有, 新加的条件并没有生效, 因为6被跳过了.
结论: js不支持一边迭代一边删元素, 他的迭代本质上等同于for(let i=0; i<length; i++), 也就是说他的key是持续计数的. 因此, 要干类似的事情时, 需要我们先保存一个临时缓冲区, 等遍历好了之后再一次性处理掉. 例如:
//例如
var ar = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
const temp=[]
for( const i in ar){
if ( (ar[i] === 5)||(ar[i]===6)) {
temp.push(i)
}
}
log("temp: ",temp)
for(const i in temp){
ar.splice(temp[i], 1);
}
log("ar: ",ar)
//=> ar: [ 1, 2, 3, 4, 6, 8, 9, 0 ]
当时我就震惊了, 这么干竟然不行, 缓存也不行, 6没有删除掉, 确错误的删除了7!
肿么办? 肿么办? 肿么办?
两个思路:
- 用filter, 但是, 这个开销有点大, 因为他会新建一个数组, 大规模搞的时候, 至少要清理内存.
- 用delete, 这个是用undefined占位的删除, 那么后续程序就要补充判断. 如果一次删的东西多, 这个判断其实还蛮难做的, 因为, 连续的删除就会有问题.
- 既然是数组, 把方向倒过来吧.
看上去就是把方向倒过来靠谱.
var ara = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
for (var i = ara.length - 1; i > -1; i--) {
if ((ara[i] === 5) || (ara[i] === 6)) {
ara.splice(i, 1);
}
}
log("ara: ", ara)
//=> ara: [ 1, 2, 3, 4, 7, 8, 9, 0 ]
OK, 搞定