删除前一个元素时,forEach会跳过元素

时间:2013-07-25 08:06:44

标签: javascript performance

我有两个数组,available_itemsrequested_items。我想删除requested_items中遗漏的available_items元素。使用forEach显然不会给出预期结果,因为即使删除了一个元素并且下一个元素具有旧索引,内部索引也会递增。

这是一个测试用例(也在this jsbin中):

var available_items = [2, 5, 9, 36, 48, 23];
var requested_items = [5, 12, 49, 30, 90, 17];
requested_items.forEach(function(v, i, a) {
  if(available_items.indexOf(v) == -1) {
    console.log("will remove " + i + ' ' + v);
    a.splice(i, 1);
  } else console.log("will keep " + i + ' ' + v);
});
console.log('Resulting request array is ' + requested_items.toString());

结果是:

"will keep 0 5"
"will remove 1 12"
"will remove 2 30"
"will remove 3 17"
"Resulting request array is 5,49,90"

这将重复数万次,因此,如果它们对性能产生负面影响,那么使用库(例如下划线)是我想要避免的。

所以,我的问题是,解决这个问题的最便宜的方法是什么?

2 个答案:

答案 0 :(得分:9)

使用for循环并向后计数,因此索引没有问题。

for(var i = requested_items.length - 1; i > 0; i--) {
   // your logic
}

它“感觉” hacky 但它可以解决问题。

答案 1 :(得分:2)

规范:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

范围是在forEach函数开始执行之前确定的 - 因此您调用了i = [0 .. 5] ... elements a[1], a[2], .. a[5]

在访问索引时确定值本身。因此,如果您删除a[1],然后跳到a[2],则跳过一个值! (在您的示例中,a[1]将为49。)

规范的最后一个重要部分:未访问已删除的索引,因此它是一个静默警告。在现实生活中的C ++数组中,您会看到等效的:'index 4 is out of range! index 5 is out of range!'

有趣的是,如果我是你,我可能会避免在循环中修改这个数组作为原则问题。在其他编程语言中,forEach循环是并行执行的,并且索引的顺序是而不是设置在一起。修改包含结构会导致未定义的行为。正如你从规范中看到的那样,这里有点......呃,你不应该这样做,但是如果你这样做会发生什么......

我的解决方案是创建第三个数组并改为使用它:

var found_items = [];
requested_items.forEach(function(v, i, a) {
  if(available_items.indexOf(v) !== -1) {
    found_items.push(v);
  }
});

保持所选样式的一种令人难以置信的hacky方法是使用while()循环在每次删除元素时保持相同的索引。

requested_items.forEach(function(v, i, a) {
  if(available_items.indexOf(v) == -1) {
    console.log("will remove " + i + ' ' + v);
    a.splice(i, 1);

    while(available_items.indexOf(a[i]) === -1) {
      console.log("will also remove " + i + ' ' + a[i]);
      a.splice(i, 1);
    }

  } else console.log("will keep " + i + ' ' + v);
});

呃,那很难看。