Array.prototype.splice()を正しくつかおう!!

jsを使っていると、Array.prototype.splice()を使うことがそこそこあるのですが、

最近まで間違って使っていたため、この機会に書いてみようと思います。

Array.prototype.splice()

まず簡単にですが、Array.prototype.splice()について説明します。

名前の通り、Arraysplice(配列の要素を取り除いて、つなぎ合わせる。)関数なのですが、

この説明だけだとイメージがわかない方もいらっしゃるかもしれませんので、簡単な例を書いておきます。

let array = ["サイ", "ヘビ", "フクロウ", "ゾウ"];
console.log(array); // ["サイ", "ヘビ", "フクロウ", "ゾウ"]
array.splice(1, 2);
console.log(array); // ["サイ", "ゾウ"]

当然ですが、インデックスが1番目の要素から順に2個の要素を削除して、配列をつなぎ合わせますので、

サイとゾウだけが配列に残ります。

すごく雑な説明なので正確に知りたい方は、ググってみてください。

次にいろんな動物が含まれる配列から、十二支に含まれない動物を削除していきたいと思います。

let animals = ["ねずみ", "うし", "とら", "う", "たつ", "み", "うま", "ひつじ", "さる", "とり", "いぬ", "いのしし", "イタチ", "ねこ"];
const excludeAnimals = ["イタチ", "ねこ"];
for (let index = 0; index < animals.length; index++) {
    if (excludeAnimals.includes(animals[index])) {
        // 十二支に含まれない動物を削除
        animals.splice(index, 1);
    }
}
console.log(animals);
// ["ねずみ", "うし", "とら", "う", "たつ", "み", "うま", "ひつじ", "さる", "とり", "いぬ", "いのしし", "ねこ"]

本来ならイタチとねこは、十二支に含まれませんが、ねこが結果に含まれてしまっていることがわかるかと思います。

簡単にですが、ループ文をトレースした結果は以下のようになります。

index animals[index] animals.length
0 ねずみ 14
1 うし 14
14
14
14
12 イタチ 14
13 13

※ `animals.length`は3行目時点での値です。

indexが12の時に、splice関数を実行した結果、animals.lengthが14 => 13に変更されます。

また、 indexが13の場合、index < animals.length ⇔ `13 < 13` ⇔ falseと評価され、

正しくループ処理ができないことをわかっていただけるかと思います。

※ ねこのインデックスも`13`でなくなります。

対応策は簡単で、こんな感じに置き換えてもらえば、正しく動作することができます。

let animals = ["ねずみ", "うし", "とら", "う", "たつ", "み", "うま", "ひつじ", "さる", "とり", "いぬ", "いのしし", "イタチ", "ねこ"];
const excludeAnimals = ["イタチ", "ねこ"];
for (let index = animals.length - 1; index > -1; index--) {
    if (excludeAnimals.includes(animals[index])) {
        // 十二支に含まれない動物を削除
        animals.splice(index, 1);
    }
}

 

以上。