7

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

这是一个测试用例(也在这个 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"

这将重复数万次,因此,如果使用库(例如下划线)对性能产生不利影响,我希望避免这种情况。

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

4

2 回答 2

12

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

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

它“感觉”很老套,但确实有效。

于 2013-07-25T08:11:57.210 回答
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]

这些值本身是在访问索引时确定的。所以如果你 delete 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);
});

呃,太丑了。

于 2013-07-25T08:39:21.540 回答