forEachループで配列から要素を削除する方法は?


97

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ステートメントに入りますが、[•••]がまだ存在していることも明らかです。


7
あなたが使用している理由はありますforEachか?アイテムを削除したい場合、最も適切な機能はfilterです。
ジョン

2
元の配列への参照を維持する必要がある場合は必要ありません。
Xotic750 2014

はい、元の配列への参照を保持したいと思います。
novicePrgrmr 2014

あなたの質問からは明らかではありません、あなたが抱えている実際の問題は何ですか?例として、おそらくjsFiddleを挙げていただけますか?あなたはおそらくあなたのためindexではなく属性を使用する必要があるようですitemsplice
Xotic750

@ Xotic750申し訳ありませんが、説明を追加しました。
novicePrgrmr 2014

回答:


235

あなたはこれをやろうとしているように見えますか?

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 使用しながら、必要に応じ砂糖に使用することもできます

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.sliceArray.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はさらにいくつかの代替手段を提供します。浅いコピーを作成してそれらを元に戻す必要はありません。特に、ジェネレータとイテレータを使用できます。ただし、サポートは現在かなり低いです。

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を取り除いた場合、Javascript NaN === NaNがfalseであるため、equalsとの比較が機能しないことです。しかし、ソリューションではそれを無視しますが、それはさらに別の不特定のエッジケースであるためです。

それで、エッジケースがまだあるソリューションでのより完全な答えがあります。最初のコード例はまだ正しいですが、述べたように、問題がないわけではありません。


回答ありがとうございます。私はあなたの解決策を使ってみましたが、それでも配列から要素が削除されていません。詳細は質問に載せます。
novicePrgrmr 2014

私の例のように、のconsole.log(review);後に置きforEachます。
Xotic750 14

4
削除する2つの連続する要素があると、これは壊れます。['a'、 'c'、 'b']
quentinadam

3
注-この答えは間違っています!foreachは、配列をインデックスごとに反復します。次のアイテムのインデックスを反復しながら要素を削除すると、変更が発生します。この例では、最初の「a」を削除すると、インデックス番号1は「c」になります。したがって、最初の「b」も評価されません。あなたがそれを削除しようとしなかったので、それはたまたま大丈夫でしたが、それは方法ではありません。配列の逆のコピーを繰り返し処理してから、元の配列の項目を削除する必要があります。
danbars

4
@ Xotic750-前のコメントで説明したように、forEachが配列内のすべての要素をループしないため、元の答え(現在は最初のコードスニペット)は間違っています。問題はforEachループで要素を削除する方法でしたが、簡単な答えはそれを行わないことです。多くの人々がそれらの回答を読んでおり、何度も盲目的に回答(特に受け入れられた回答)をコピーしているため、コードの欠陥に注意することが重要です。逆whileループは最も単純で最も効率的で最も読みやすいソリューションであり、受け入れられる答えであると
思い

37

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

10

Xotic750の答えはいくつかの良い点と可能な解決策を提供し、時にはシンプルな方が良いです

反復されている配列が反復自体で変更されている(つまり、アイテムの削除=>インデックスの変更)ことがわかっているため、最も単純なロジックは、古い形式forC言語のアラカルト)で後方に戻ることです。

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());

あなたが本当にそれを考えているなら、a forEachforループの単なる構文上の糖...


1

代わりにindexOfを使用してこれを行うこともできます

var i = review.indexOf('\u2022 \u2022 \u2022');
if (i !== -1) review.splice(i,1);

1

条件を使用して配列から削除し、配列から項目を削除した別の配列を作成することを理解しました。は正しい?

これはどう?

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


0

以下は、あなたの特殊文字とは異なるすべての要素を提供します!

review = jQuery.grep( review, function ( value ) {
    return ( value !== '\u2022 \u2022 \u2022' );
} );

0

以下にその方法を示します。

review.forEach(function(p,index,object){
   if(review[index] === '\u2022 \u2022 \u2022'){
      console.log('YippeeeE!!!!!!!!!!!!!!!!')
      review.splice(index, 1);
   }
});

1
私はそうではないと思います。私はpがインデックスであると想定してコードを変更しましたが、今ではifステートメントにさえ入っていません。
novicePrgrmr 2014

3
@WhoCares仕様ecma-international.org/ecma-262/5.1/#sec-15.4.4.18が表示されるはずです。callBack関数の引数はitem, index, object
Xotic750

-1

問題は、配列をスプライスするたびに値のキーまたはインデックスが明らかに変化するため、針が連続して「true」を押すと、スタックの次の値を見逃す可能性があることです。

あなたができることを打ち消すために:

let review = ['a','a','a','b','c','a']

let tempArray = Array.from(review) 

let counter = 0

tempArray.forEach((item,index,obj)=>{
          if(item === 'a'){

            review.splice(index - counter, 1)
            counter++
           
           }

 }) 
 
 console.log(review)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.