从传递给forEach的回调中拼接Javascript数组

问题描述 投票:17回答:5

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

//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
5个回答
36
投票

让我们看看为什么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' ]

13
投票

一种可能性是使用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之后,数组将只包含ab

一个jsFiddle demo can be found here


1
投票

另一种可能性是使用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


0
投票

在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);
});

-1
投票

如果我们要删除特定索引处的两个元素并继续从immediate元素迭代,那么上面提到的所有答案都会失败或者不保留原始数组要传递到别处。假设,我有一个数组

vehicles = [{make: ford, model: mustang}, 
            {make: chevy, model: camaro}, 
            {make: chevy, model: camaro},
            {make: ford, model: mustang},
            {make: chevy, model: camaro}]

如果有福特和雪佛兰的连续组合,我想要拼掉两个元素。

vehicles.forEach(function (vehicle) {
         if (vehicle) {
              var index = vehicles.indexOf(vehicle);
              var flag = vehicle.make=== "ford" && vehicles[index + 1].make=== "chevy";
              if (flag) {
                  //Array.Prototype.forEach() wouldn't update the iteration index after splice
                  vehicles.splice(index, 2, null);
              }
          }
});

所以这样我用null替换了这对拼接元素,这样我就可以适应forEach()的非更新迭代索引。然后,我可以在迭代完成后清理任何插入的空数组,并准备好传递数组。

//After all the iteration is done, we clear all the inserted null
vehicles = [].concat(vehicles.filter(Boolean));

这可以是更好的方式,不妨碍任何事情,并绝对解决这种javascript的怪异行为。

© www.soinside.com 2019 - 2024. All rights reserved.