他のプログラム言語と同じく、JavaScriptにも配列オブジェクトがあります。JavaScrip初心者の私は、さっそく配列で遊んでみました。そのとき、配列から特定の要素を削除するのにちょっとしたコツがいることを気づいたのでメモします。
準備:JavaScriptの配列
配列を作成する方法はいろいろあります。最も単純な方法は、配列オブジェクトに要素を[ ]で書いて定義してしまうことでしょう。例えば
var arr = [0,1,2,3,4,5];
のように書きます。
例題:偶数の要素を配列から削除する
例えば以下のような配列があります。
var arr = [1,2,3,4,5,6,7,8,9,10];
この10個の要素で構成される配列から、偶数の要素を削除してみたいと思います。
方法その1:delete
要素を削除する方法の一つにdeleteコマンドがあります。例えば、
delete arr[i];
のように 書くとi番目の要素を削除します。
deleteの利用例は以下の通りです。
var arr = [1,2,3,4,5,6,7,8,9,10];
for (var i = 0; i<arr.length; i++){
if (arr[i]%2 === 0){
delete arr[i];
}
}
このコードの実行結果をコンソールに表示してみると、
console.log(arr);
1,,3,,5,,7,,9,
たしかに偶数の要素が配列から消えました。
でも少し違和感があります。なぜ1と3の間にカンマが二つあるんでしょう。
この結果では、要素の値が削除されています。でも実は、要素そのものは削除されていません。その証拠に要素の数をコンソールに表示してみると、
console.log(arr.length);
10
まだ、要素の数が10のままです。どうも連続するカンマの間には、nullがあるようです。方法その2では、これを確かめてみます。
方法その2:nullへの置換
nullは有効な値が存在しないことを示します。例えば、
arr[i] = null;
のように書くと、i番目の要素をnullへ置き換えます。
nullへの置換の利用例は以下の通りです。
var arr = [1,2,3,4,5,6,7,8,9,10];
for (var i = 0; i<arr.length; i++){
if (arr[i]%2 === 0){
arr[i] = null;
}
}
このコードの実行結果をコンソールに表示してみると、
console.log(arr);
1,,3,,5,,7,,9,
配列の長さは、
console.log(arr.length);
10
先ほど予想したとおり、deleteを使った場合と同じ結果になりました。
これら二つの方法のどちらかを使えば、配列の長さを変えずに配列内の特定の要素を削除できます。
でも、配列から要素を削除したいときは、要素の値だけでなく、その要素自体も削除したい場合があります。次は、その方法を考えてみましょう。
方法その3:splice(ただし失敗例)
spliceは配列から特定の要素自体を削除します。例えば、
arr.splice(i,j);
のように書くとi番目からj個の要素を削除します。
spliceの利用例は以下の通りです。
var arr = [1,2,3,4,5,6,7,8,9,10];
for (var i = 0; i<arr.length; i++){
if (arr[i]%2 === 0){
arr.splice(i,1);
}
}
このコードの実行結果をコンソールに表示してみると、
console.log(arr);
1,3,5,7,9
配列の長さは、
console.log(arr.length);
5
となりました。これで、偶数の値を持つ要素を配列から削除できました。
でも、このコードは、例のような「奇数と偶数が交互に並んでいる場合」でのみ
意図した結果が得られます。
別のデータセットで試してみましょう。
var arr = [1,2,4,3,6,8,5,10,12,7,14,16,9,18,20];
for (var i = 0; i<arr.length; i++){
if (arr[i]%2 === 0){
arr.splice(i,1);
}
}
このコードの実行結果をコンソールに表示してみると、
console.log(arr);
1,4,3,8,5,12,7,16,9,20
配列の長さは、
console.log(arr.length);
10
となりました。これで、偶数の値を持つ要素を配列から削除できていません。
何が起きたのかを考えるのに、奇妙な出力結果が役立ちそうです。
2番目の要素だった2は削除されましたたが、3番目の要素だった4はそのままです。
5番目の要素だった6は削除されましたが、6番目の要素だった8も残っています。
どうやら二つの連続する偶数のうち、先に出現した方は削除されましたが、後で出現した方は残っているように見えます。
方法その4:splice (また失敗例)
方法その3のコードを改善してみます。
方法その3の問題点は、偶数の値を持つ要素を削除した後で、その次の要素について奇数か偶数かの判定をしないことにありました。そこで、偶数の値を持つ要素を見つけた場合に、配列のインデックスが変わらないようにしてみましょう。そのため、for文内の変数iとは異なる変数を作成します。これをインデックスとしてみましょう。
var arr = [1,2,4,3,6,8,5,10,12,7,14,16,9,18,20];
var p = 0;
for (var i = 0; i<arr.length; i++){
if (arr[p]%2 === 0){
arr.splice(p,1);
} else{
p += 1;
}
}
このコードの実行結果をコンソールに表示してみると、
console.log(arr);
1,3,5,7,14,16,9,18,20
配列の長さは、
console.log(arr.length);
9
予想通りの部分でそうでない部分ができてしまいました。
出力結果の前半は予想通り、偶数の値を持つ要素が連続していても、両方削除できました。問題は後半です。偶数の値を持つ要素が連続している場所で、両方とも削除できていません。
念のため、pの値がどこまで進んだかを調べてみます。
console.log(p);
3
pは0から始まり、奇数の値を持つ要素を見つけるたびに、数が1増えることになっています。pが3で終わっているということは、配列のうち、1、3、5の値を持つ要素は見つけているが、それ以降は処理がなされていないことになります。
どうして途中で計算が止まってしまったのでしょう。
これは恐らく、for文の範囲設定でi<arr.lengthという範囲設定をしていることに由来しています。forのループが一回進むごとに、配列全体の長さが再評価されていると仮定してみます。そうすると、現在のコードは、配列の長さが次第に短くなるので、最後の要素まで計算が繰り返されないかもしれません。もしこれが原因ならば、最初の配列の長さの分だけ確実に計算が繰り返されるようにすればいいはずです。
方法その5:splice(やっと成功例)
方法その4では、偶数の要素が連続していても、全ての要素を確実に削除できるようになりました。一方、計算が途中で終わってしまうという予想外の展開も見られました。計算回数を、計算開始時の配列の長さに設定してもう一度計算してみます。
var arr = [1,2,4,3,6,8,5,10,12,7,14,16,9,18,20];
var p = 0;
var ini = arr.length;
for (var i = 0; i<ini; i++){
if (arr[p]%2 === 0){
arr.splice(p,1);
} else{
p += 1;
}
}
このコードの実行結果をコンソールに表示してみると、
console.log(arr);
1,3,5,7,9
配列の長さは、
console.log(arr.length);
5
うまくいきました。このコードを使用すると、配列から偶数の値を持つ要素を全て削除できそうです。また、if文の条件を変更すれば、偶数以外でも、特定の条件を満たす要素を削除できます。