如何从 forEach 循环中的数组中删除元素?
Posted
技术标签:
【中文标题】如何从 forEach 循环中的数组中删除元素?【英文标题】:How to remove element from array in forEach loop? 【发布时间】:2014-09-08 21:05:51 【问题描述】:我正在尝试在forEach
循环中删除数组中的一个元素,但是我遇到了我所看到的标准解决方案的问题。
这是我目前正在尝试的:
review.forEach(function(p)
if(p === '\u2022 \u2022 \u2022')
console.log('YippeeeE!!!!!!!!!!!!!!!!')
review.splice(p, 1);
);
我知道它正在进入if
,因为我在控制台中看到了YippeeeeeE!!!!!!!!!!!!!
。
我的问题:我知道我的 for 循环和 if 逻辑是合理的,但我从数组中删除当前元素的尝试失败了。
更新:
试过Xotic750的回答,还是没有去掉元素:
这是我代码中的函数:
review.forEach(function (item, index, object)
if (item === '\u2022 \u2022 \u2022')
console.log('YippeeeE!!!!!!!!!!!!!!!!')
object.splice(index, 1);
console.log('[' + item + ']');
);
这是数组仍未删除的输出:
[Scott McNeil]
[reviewed 4 months ago]
[ Mitsubishi is AMAZING!!!]
YippeeeE!!!!!!!!!!!!!!!!
[• • •]
很明显它会按照指示进入 if 语句,但也很明显 [• • •] 仍然存在。
【问题讨论】:
您使用forEach
有什么原因吗?如果要删除项目,最合适的函数是filter
。
如果您需要保留对原始数组的引用,则不需要。
是的,我们希望保留对原始数组的引用。
从您的问题中不清楚,您遇到的实际问题是什么?你能举个例子,也许是一个jsFiddle?看来您可能应该使用 index
属性而不是 item
作为您的 splice
@Xotic750 抱歉,补充说明。
【参考方案1】:
您似乎正在尝试这样做?
使用Array.prototype.splice 迭代和改变一个数组
var pre = document.getElementById('out');
function log(result)
pre.appendChild(document.createTextNode(result + '\n'));
var review = ['a', 'b', 'c', 'b', 'a'];
review.forEach(function(item, index, object)
if (item === 'a')
object.splice(index, 1);
);
log(review);
<pre id="out"></pre>
这适用于您没有 2 个与相邻数组项相同的值的简单情况,否则您会遇到此问题。
var pre = document.getElementById('out');
function log(result)
pre.appendChild(document.createTextNode(result + '\n'));
var review = ['a', 'a', 'b', 'c', 'b', 'a', 'a'];
review.forEach(function(item, index, object)
if (item === 'a')
object.splice(index, 1);
);
log(review);
<pre id="out"></pre>
那么当迭代和改变一个数组时,我们能做些什么来解决这个问题呢?那么通常的解决方案是反向工作。使用 ES3 while,但如果愿意,您可以使用 for 糖
var pre = document.getElementById('out');
function log(result)
pre.appendChild(document.createTextNode(result + '\n'));
var review = ['a' ,'a', 'b', 'c', 'b', 'a', 'a'],
index = review.length - 1;
while (index >= 0)
if (review[index] === 'a')
review.splice(index, 1);
index -= 1;
log(review);
<pre id="out"></pre>
好的,但是您想使用 ES5 迭代方法。好吧,选择是使用Array.prototype.filter,但这不会改变原始数组而是创建一个新数组,因此虽然您可以获得正确答案,但它似乎不是您指定的。
我们也可以使用 ES5 Array.prototype.reduceRight,而不是因为它的减少属性,而是它的迭代属性,即反向迭代。
var pre = document.getElementById('out');
function log(result)
pre.appendChild(document.createTextNode(result + '\n'));
var review = ['a', 'a', 'b', 'c', 'b', 'a', 'a'];
review.reduceRight(function(acc, item, index, object)
if (item === 'a')
object.splice(index, 1);
, []);
log(review);
<pre id="out"></pre>
或者我们可以像这样使用 ES5 Array.protoype.indexOf。
var pre = document.getElementById('out');
function log(result)
pre.appendChild(document.createTextNode(result + '\n'));
var review = ['a', 'a', 'b', 'c', 'b', 'a', 'a'],
index = review.indexOf('a');
while (index !== -1)
review.splice(index, 1);
index = review.indexOf('a');
log(review);
<pre id="out"></pre>
但是你特别想使用 ES5 Array.prototype.forEach,那我们该怎么办呢?好吧,我们需要使用Array.prototype.slice 来制作数组的浅表副本,并使用Array.prototype.reverse 来逆向操作来改变原始数组。
var pre = document.getElementById('out');
function log(result)
pre.appendChild(document.createTextNode(result + '\n'));
var review = ['a', 'a', 'b', 'c', 'b', 'a', 'a'];
review.slice().reverse().forEach(function(item, index, object)
if (item === 'a')
review.splice(object.length - 1 - index, 1);
);
log(review);
<pre id="out"></pre>
最后,ES6 为我们提供了一些进一步的选择,我们不需要制作浅拷贝和反转它们。值得注意的是,我们可以使用Generators and Iterators。但是目前支持率很低。
var pre = document.getElementById('out');
function log(result)
pre.appendChild(document.createTextNode(result + '\n'));
function* reverseKeys(arr)
var key = arr.length - 1;
while (key >= 0)
yield key;
key -= 1;
var review = ['a', 'a', 'b', 'c', 'b', 'a', 'a'];
for (var index of reverseKeys(review))
if (review[index] === 'a')
review.splice(index, 1);
log(review);
<pre id="out"></pre>
以上所有内容中需要注意的是,如果您从数组中剥离 NaN,那么与 equals 进行比较将不起作用,因为在 javascript 中 NaN === NaN
是错误的。但我们将在解决方案中忽略这一点,因为它是另一个未指定的边缘情况。
所以我们有了它,一个更完整的答案,解决方案仍然存在边缘情况。第一个代码示例仍然正确,但如上所述,它并非没有问题。
【讨论】:
感谢您的回答。我尝试使用您的解决方案,但它仍然没有从数组中删除元素。我会在问题中提出详细信息。 把console.log(review);
放在forEach
之后,就像我的例子一样。
小心,如果要删除两个连续的元素,这会中断: var review = ['a', 'a', 'c', 'b', 'a'];将产生 ['a', 'c', 'b']
注意 - 这个答案是错误的! foreach 按索引遍历数组。一旦您在迭代以下项目的索引时删除元素,就会发生变化。在此示例中,一旦删除第一个“a”,索引号 1 现在变为“c”。因此,甚至不评估第一个“b”。既然你没有尝试删除它,它只是碰巧没问题,但事实并非如此。您应该遍历数组的反向副本,然后删除原始数组中的项目。
@Xotic750 - 最初的答案(现在是第一个代码 sn-p)是错误的,因为 forEach 不会循环遍历数组中的所有元素,正如我在之前的评论中解释的那样。我知道问题是如何删除 forEach 循环中的元素,但简单的答案是您不这样做。由于很多人都在阅读这些答案,并且很多时候都在盲目地复制答案(尤其是已接受的答案),因此注意代码中的缺陷非常重要。我认为反向while循环是最简单、最有效、最易读的解决方案,应该是公认的答案【参考方案2】:
使用Array.prototype.filter
代替forEach
:
var pre = document.getElementById('out');
function log(result)
pre.appendChild(document.createTextNode(result + '\n'));
var review = ['a', 'b', 'c', 'b', 'a', 'e'];
review = review.filter(item => item !== 'a');
log(review);
【讨论】:
【参考方案3】:虽然Xotic750's answer 提供了几个优点和可能的解决方案,但有时simple is better。
您知道被迭代的数组在迭代本身中发生了变异(即删除一个项目 => 索引更改),因此最简单的逻辑是在老式 for
中倒退(à la C 语言):
let arr = ['a', 'a', 'b', 'c', 'b', 'a', 'a'];
for (let i = arr.length - 1; i >= 0; i--)
if (arr[i] === 'a')
arr.splice(i, 1);
document.body.append(arr.join());
如果您认真考虑一下,forEach
只是 for
循环的语法糖...所以如果它对您没有帮助,请不要再为它头疼了。
【讨论】:
【参考方案4】:我了解到您想使用条件从数组中删除,并让另一个数组从数组中删除项目。对吗?
这个怎么样?
var review = ['a', 'b', 'c', 'ab', 'bc'];
var filtered = [];
for(var i=0; i < review.length;)
if(review[i].charAt(0) == 'a')
filtered.push(review.splice(i,1)[0]);
else
i++;
console.log("review", review);
console.log("filtered", filtered);
希望对您有所帮助...
顺便说一句,我将“for-loop”与“forEach”进行了比较。
如果在字符串包含'f'的情况下删除,则结果不同。
var review = ["of", "concat", "copyWithin", "entries", "every", "fill", "filter", "find", "findIndex", "flatMap", "flatten", "forEach", "includes", "indexOf", "join", "keys", "lastIndexOf", "map", "pop", "push", "reduce", "reduceRight", "reverse", "shift", "slice", "some", "sort", "splice", "toLocaleString", "toSource", "toString", "unshift", "values"];
var filtered = [];
for(var i=0; i < review.length;)
if( review[i].includes('f'))
filtered.push(review.splice(i,1)[0]);
else
i++;
console.log("review", review);
console.log("filtered", filtered);
/**
* review [ "concat", "copyWithin", "entries", "every", "includes", "join", "keys", "map", "pop", "push", "reduce", "reduceRight", "reverse", "slice", "some", "sort", "splice", "toLocaleString", "toSource", "toString", "values"]
*/
console.log("========================================================");
review = ["of", "concat", "copyWithin", "entries", "every", "fill", "filter", "find", "findIndex", "flatMap", "flatten", "forEach", "includes", "indexOf", "join", "keys", "lastIndexOf", "map", "pop", "push", "reduce", "reduceRight", "reverse", "shift", "slice", "some", "sort", "splice", "toLocaleString", "toSource", "toString", "unshift", "values"];
filtered = [];
review.forEach(function(item,i, object)
if( item.includes('f'))
filtered.push(object.splice(i,1)[0]);
);
console.log("-----------------------------------------");
console.log("review", review);
console.log("filtered", filtered);
/**
* review [ "concat", "copyWithin", "entries", "every", "filter", "findIndex", "flatten", "includes", "join", "keys", "map", "pop", "push", "reduce", "reduceRight", "reverse", "slice", "some", "sort", "splice", "toLocaleString", "toSource", "toString", "values"]
*/
并且每次迭代删除,结果也不同。
var review = ["of", "concat", "copyWithin", "entries", "every", "fill", "filter", "find", "findIndex", "flatMap", "flatten", "forEach", "includes", "indexOf", "join", "keys", "lastIndexOf", "map", "pop", "push", "reduce", "reduceRight", "reverse", "shift", "slice", "some", "sort", "splice", "toLocaleString", "toSource", "toString", "unshift", "values"];
var filtered = [];
for(var i=0; i < review.length;)
filtered.push(review.splice(i,1)[0]);
console.log("review", review);
console.log("filtered", filtered);
console.log("========================================================");
review = ["of", "concat", "copyWithin", "entries", "every", "fill", "filter", "find", "findIndex", "flatMap", "flatten", "forEach", "includes", "indexOf", "join", "keys", "lastIndexOf", "map", "pop", "push", "reduce", "reduceRight", "reverse", "shift", "slice", "some", "sort", "splice", "toLocaleString", "toSource", "toString", "unshift", "values"];
filtered = [];
review.forEach(function(item,i, object)
filtered.push(object.splice(i,1)[0]);
);
console.log("-----------------------------------------");
console.log("review", review);
console.log("filtered", filtered);
【讨论】:
【参考方案5】:你也可以使用 indexOf 来代替
var i = review.indexOf('\u2022 \u2022 \u2022');
if (i !== -1) review.splice(i,1);
【讨论】:
【参考方案6】:下面将给你所有不等于你的特殊字符的元素!
review = jQuery.grep( review, function ( value )
return ( value !== '\u2022 \u2022 \u2022' );
);
【讨论】:
【参考方案7】:你应该这样做:
review.forEach(function(p,index,object)
if(review[index] === '\u2022 \u2022 \u2022')
console.log('YippeeeE!!!!!!!!!!!!!!!!')
review.splice(index, 1);
);
【讨论】:
我不认为是这样。我更改了我的代码,假设 p 是一个索引,现在它甚至没有进入if
语句。
@WhoCares 你应该看到规范ecma-international.org/ecma-262/5.1/#sec-15.4.4.18回调函数参数是item, index, object
以上是关于如何从 forEach 循环中的数组中删除元素?的主要内容,如果未能解决你的问题,请参考以下文章
js forEach参数详解,forEach与for循环区别,forEach中如何删除数组元素
如何仅使用 ForEach 而不是 List 从动态范围中删除