【问题标题】:Splicing a Javascript array from within the callback passed to forEach在传递给 forEach 的回调中拼接一个 Javascript 数组
【发布时间】:2014-03-15 17:15:21
【问题描述】:

我有这段代码应该遍历数组中的每个项目,根据某些条件删除项目:

//iterate over all items in an array
//if the item is "b", remove it.

var array = ["a", "b", "c"];

array.forEach(function(item) {
  if(item === "b") {
    array.splice(array.indexOf(item), 1);
  }

  console.log(item);
});

期望的输出:

a
b
c

实际输出:

a
b

显然,原生 forEach 方法不会在每次迭代后检查项目是否已被删除,因此如果是,则跳过下一个项目。除了重写 forEach 方法或实现我自己的类来代替数组之外,还有更好的方法吗?

编辑 - 根据我的评论,我想解决方案是只使用标准 for 循环。如果您有更好的方法,请随时回答。

【问题讨论】:

  • 其实我明白他们为什么不尝试支持这个功能 - 你需要检查删除的项目是否是当前项目,否则你可能会不必要地向后跳过(例如,如果检查只是基于长度是否减少了一个或什么)。实施起来似乎太复杂了。

标签: javascript arrays foreach


【解决方案1】:

让我们看看为什么 JavaScript 会这样。根据ECMAScript standard specification for Array.prototype.forEach

当您删除索引 1 处的元素时,索引 2 处的元素将成为索引 1 处的元素,并且该对象不存在索引 2。

现在,JavaScript 在对象中查找元素 2,但未找到,因此跳过了函数调用。

这就是为什么您只能看到ab


执行此操作的实际方法是使用Array.prototype.filter

var array = ["a", "b", "c"];

array = array.filter(function(currentChar) {
    console.log(currentChar);   // a, b, c on separate lines
    return currentChar !== "b";
});
console.log(array);             // [ 'a', 'c' ]

【讨论】:

  • +1 但是,如果您需要对数组项进行任何更改(就像我在我的情况下所做的那样 - 对象数组),以及过滤掉项,最好使用reduce
  • ?在return 语句之后,您无法将任何内容记录到控制台
  • @Blauhirn 不,你不能。
  • @JuanBiscaia 这是我的观点。在上面的例子中,有一个日志语句返回之后。
  • @Blauhirn 哦,好的,抱歉,我很着急,没注意到你没有问问题,我的错。
【解决方案2】:

一种可能性是使用array.slice(0) 函数,该函数创建数组的副本 (clone),从而将迭代与删除分开。

那么对使用array.forEach 的原始方法的唯一更改就是将其更改为array.slice(0).forEach,它会起作用:

array.slice(0).forEach(function(item) {
    if(item === "b") {
        array.splice(array.indexOf(item), 1);
    }
    alert(item)
});

在forEach之后,数组将只包含ac

jsFiddle demo can be found here

【讨论】:

  • 另外,如果你使用 lodash/underscore,你可以使用_.clone(array).forEach(function (item) { ... })
  • 这很有趣,但它涉及每次迭代的查找,尽管搜索的数组的大小会减小。编写一个 for 循环并注意索引会更快。
【解决方案3】:

在thefourtheye 的回答中使用Array.prototype.filter 是一个不错的方法,但这也可以通过while 循环来完成。例如:

const array = ["a", "b", "c"];
let i = 0;

while (i < array.length) {
    const item = array[i];

    if (item === "b") {
        array.splice(i, 1);
    } else {
        i += 1;
    }

    console.log(item);
});

【讨论】:

    【解决方案4】:

    另一种可能性是使用array.reduceRight 函数来避免跳过:

    //iterate over all items in an array from right to left
    //if the item is "b", remove it.
    
    const array = ["a", "b", "c"];
    
    array.reduceRight((_, item, i) => {
        if(item === "b") {
            array.splice(i, 1);
        }
    
    });
    
    console.log(array);
    

    reduceRight 之后,数组将只包含ac

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-24
      • 2011-10-24
      • 1970-01-01
      • 2020-01-22
      • 1970-01-01
      • 2016-08-19
      相关资源
      最近更新 更多