Javascriptで配列にある値が存在するかどうかを確認する方法について
以前配列にある値が存在するかどうかをfor文を使わずに調べる方法を考えてみたという記事を書きました。この記事は「やろうと思えばこういう方法でもできるなぁ」というアイデアについて書いた記事だったのですが、Javascriptで配列にある値が存在するかどうかを確認する一般的な方法を探して当該記事を訪問していただくことが多いので改めてこの方法について書いてみたいと思います。
といってもJavascriptには"Array.contain"のような「まさにそのためにある」というメソッドが用意されているわけではないのでいずれにしろ方法の紹介ということになってしまいますが。
次の3つのパターンに分けて書きたいと思います。
- ECMAScript 5が使える場合
- jQueryが使える場合
- 上記のどちらも使わない場合
1.ECMAScript 5が使える場合
ECMAScript 5では配列に"indexOf"というメソッドがあります。これは引数に与えられた値がその配列の中で最初に登場する位置を返します。
var testArray = [3, 8, 13, true, 'あいうえお', 8, 10]; window.alert(testArray.indexOf(8)); // 1がアラートされる window.alert(testArray.indexOf('あいうえお')); // 4がアラートされる
"indexOf"は要素が配列内で見つからなかった場合は-1を返します。そのためこの返り値が-1であれば存在しない、0以上であれば存在すると判断出来ます。
var testArray = [3, 8, 13, true, 'あいうえお', 8, 10]; if(testArray.indexOf(13) >= 0){ window.alert('存在する!'); // アラートされる };
ちなみに"indexOf"は第2引数で探索を開始する位置を指定出来ます。
var testArray = [3, 8, 13, true, 'あいうえお', 8, 10]; if(testArray.indexOf(13, 3) >= 0){ window.alert('存在する!'); // trueの位置から後ろへ探索することになるのでアラートされない };
また存在を確認するだけならどちらでも同じになりますが配列の末尾から先頭に向かって逆方向に探索する"lastIndexOf"というメソッドもあります。
var testArray = [3, 8, 13, true, 'あいうえお', 8, 10]; window.alert(testArray.lastIndexOf(8)); // 末尾に近い方の8が先に見つかるので5がアラートされる window.alert(testArray.lastIndexOf('あいうえお')); // 4がアラートされる if(testArray.lastIndexOf(13) >= 0){ window.alert('存在する!'); // アラートされる };
ECMAScript 5は簡潔に書けるのですがIE8では動かないので実際には次のjQueryを使用する方法のほうが使用機会が多いかもしれません。
2.jQueryが使える場合
jQueryには"inArray"というメソッドが用意されています。これは上で書いたECMAScript 5の"indexOf"と機能的には同じで配列内で指定の要素が最初に登場する位置を返します。ただしあくまでもjQueryのメソッドであってArray.prototypeにメソッドを追加するわけではないので探索先の配列も引数で受け取ることになります。
var testArray = [3, 8, 13, true, 'あいうえお', 8, 10]; window.alert($.inArray(8, testArray)); // 1がアラートされる window.alert($.inArray('あいうえお', testArray)); // 4がアラートされる
ECMAScript 5の"indexOf"と同様に配列内で要素が見つからなかった場合は-1を返すので0との比較で存在確認ができます。
var testArray = [3, 8, 13, true, 'あいうえお', 8, 10]; if($.inArray(13, testArray) >= 0){ window.alert('存在する!'); // アラートされる };
さらに第3引数で探索開始位置も指定できます。
var testArray = [3, 8, 13, true, 'あいうえお', 8, 10]; if($.inArray(13, testArray, 3) >= 0){ window.alert('存在する!'); // trueから末尾に向かって探索するのでアラートされない };
3.上記のどちらも使わない場合
どちらも使わない場合は先頭から比較していき片っ端から探すといういわゆる線形探索を行うことになります。
var testArray = [3, 8, 13, true, 'あいうえお', 8, 10]; for(var i = 0; i < testArray.length; i++){ if(testArray[i] === 8){ // 「8」を探す場合 window.alert('存在する!'); // アラートされる break; }; };
jQueryの"inArray"メソッドも内部はこの方法で実装されているのですがjQueryでは少し工夫されています。
jQueryのソースでは下のようになっています。日本語コメントは私が書いたものです。
//jQuery1.9.0より引用 (684行目) inArray: function( elem, arr, i ) { var len; if ( arr ) { if ( core_indexOf ) { // ECMAScript 5のindexOfが使える場合は return core_indexOf.call( arr, elem, i ); // それを使う } len = arr.length; i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; // 探索開始位置の指定 for ( ; i < len; i++ ) { // Skip accessing in sparse arrays if ( i in arr && arr[ i ] === elem ) { return i; } } } return -1; }
"Skip accessing in sparse arrays"と書かれていますが、これはスカスカの配列を効率よく探索するための工夫です。
Javascriptでは
var testArray = []; testArray[1000] = 10;
という書き方もできるためfor文を愚直にまわすと上の例ではtestArray[0]からtestArray[999]まで1000回無駄に比較を行わなければならなくなります。
そこで先に
i in arr
という記述でなにかしら値が設定されているかどうか(プロパティが存在しているかどうか)を確認し、もし値があれば比較してみるという流れになっています。わざわざundefinedを呼び出しundefinedと値を比較する処理に比べるとinを使ってそもそもundefinedかどうかを確認する方が速いからだと思われます。
なので本項の最初のコードにこの工夫を施すと
var testArray = [3, 8, 13, true, 'あいうえお', 8, 10]; for(var i = 0, len = testArray.length; i < len; i++){ if(i in testArray && testArray[i] === 8){ window.alert('存在する!'); break; }; };
となります。この場合は値が全て埋まっているので全く意味がありませんが。
以上がJavascriptで配列にある値が存在するかどうか確認する方法の整理でした。
余談ですが私はJavascriptの勉強を始めたばかりの頃、forループからreturnを使って直接関数外へ抜けるのが問題ないのかどうかよく分かっていない時期がありました。C言語では好ましくないという意見の方もいらっしゃってJavascriptではどうなんだろう・・と悩んでいた時に上記のjQuery.inArrayのコードを見て
「jQueryで使われているくらいだから問題ないんだ!」
と思いそれ以後自分でも使うようになりました。
という思い出話でした(^_^;)