質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

695閲覧

.forEach()内で.test()を用いて文字列検索を行う際のテスト結果のブレについて

yashiking

総合スコア8

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

2グッド

1クリップ

投稿2018/05/15 07:01

javascriptを用いて、入れ子構造(?)になっている配列から特定の文字列が含まれる単語を見つけるコードを書いています。
実行場所はchromeです。

<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>

をhtmlファイルの冒頭に記載しています。

正規表現を用いて、'.3'が含まれる(..3.)単語を探しています。
以下のコードを試しており、4.3.1,4.3.2,4.3.2,4.4.3の時に"if後は"が含まれる文章をconsoleさせようとしています。

javascript

1var dotfour = ['4.4.1','4.4.2','4.4.3']; 2var dotthree = ['4.3.1','4.3.2','4.3.3']; 3var four = ['4.1','4.2',dotthree,dotfour,'4.5']; 4var array = ['1','2','3',four,'5']; 5 6function test_list(array,loop) { 7 var test_reg = new RegExp('.3' , 'g'); 8 array.forEach(function (list,index) { 9 console.log(list,loop); 10 if (typeof list === "object"){ 11 if (loop === 1){ 12 test_list(list,loop + 1); 13 }else if(loop === 2){ 14 test_list(list,loop +1); 15 } 16 }else if(typeof list === "string"){ 17 console.log(list + 'の時、if前は' + test_reg.test(list.toLocaleLowerCase())); 18 if (test_reg.test(list.toLocaleLowerCase())){ 19 console.log(list + 'の時、if後は' + test_reg.test(list.toLocaleLowerCase())); 20 } 21 } 22 }); 23 if (loop === 1){ 24 console.log('end'); 25 } 26} 27$(function () { 28 test_list(array,1); 29});

しかし、結果は、

javascript

11 1 21の時、if前はfalse 32 1 42の時、if前はfalse 53 1 63の時、if前はfalse 7(5) ["4.1", "4.2", Array(3), Array(3), "4.5"] 1 84.1 2 94.1の時、if前はfalse 104.2 2 114.2の時、if前はfalse 12(3) ["4.3.1", "4.3.2", "4.3.3"] 2 134.3.1 3 144.3.1の時、if前はtrue 154.3.2 3 164.3.2の時、if前はtrue 174.3.3 3 184.3.3の時、if前はtrue 194.3.3の時、if後はfalse 20(3) ["4.4.1", "4.4.2", "4.4.3"] 2 214.4.1 3 224.4.1の時、if前はfalse 234.4.2 3 244.4.2の時、if前はfalse 254.4.3 3 264.4.3の時、if前はtrue 274.5 2 284.5の時、if前はfalse 295 1 305の時、if前はfalse 31end

というようになります。
"if前はtrue"と記載されている部分で、trueと表記するのと同じ"test_reg.test(list.toLocaleLowerCase())"をifの条件にしているのに、そのifの中にあるconsoleを実行していない部分があります。

しかし、コードの一部分を

javascript

1}else if(typeof list === "string"){ 2 var test_result = test_reg.test(list.toLocaleLowerCase()); 3 console.log(list + 'の時、if前は' + test_result); 4 if (test_result){ 5 console.log(list + 'の時、if後は' + test_reg.test(list.toLocaleLowerCase())); 6 } 7 }

に変更すると、(正規表現による探索結果を変数に代入し、その変数を利用するようにしています。ifの中は正規表現による結果をそのままconsoleする設定です。)

javascript

14.3.1 3 24.3.1の時、if前はtrue 34.3.1の時、if後はfalse 44.3.2 3 54.3.2の時、if前はtrue 64.3.2の時、if後はfalse 74.3.3 3 84.3.3の時、if前はtrue 94.3.3の時、if後はtrue

というように、if内に入る流れは想定通りの実行がなされます。
しかし、if内部では、元々trueだった探索結果がfalseになっています。
つまり、else if内でtest_reg.test(list.toLocaleLowerCase())の結果がif前後で変化しているのですが、これはなぜでしょうか。理由がわかりません。

forEachで他の要素を参照して、listが書き換えられてしまって結果が変わってしまったのかとも思っていますが、forEachの仕様のページ(https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/foreach)を確認してもそのような、各要素を見ている途中に他の要素に飛ぶというような記載を見つけることができませんでしたし、listの値は変更されていない(if前とif後で参照している数字が変わっていない)ので、そうではないと思います。

実装としては変更後のやり方で目的は達成できるので良いのですが、気になるので、教えていた開けると嬉しいです。
よろしくお願いします。

m.ts10806, manzyun👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

RegExpオブジェクトをグローバルサーチ(g)で複数回使用する場合に、オブジェクト内部のlastIndexプロパティの値が更新されます。

RegExp.lastIndex - JavaScript | MDN - Mozilla

このプロパティは、正規表現が、グローバルサーチを示す "g" を使用した場合にのみ、セットされます。 or the "y" flag to indicate a sticky search. 以下のルールが適用されます。:

・lastIndex が文字列の長さよりも大きければ、test() 及び exec() は失敗し、lastIndex は 0 にセットされます。
・lastIndex が文字列の長さと等しく、かつ、正規表現が空文字列にマッチする場合には、正規表現は lastIndex の始まりの入力にマッチします。
・lastIndex が文字列の長さと等しく、かつ、正規表現が空文字列にマッチしない場合、正規表現は入力にマッチせず、lastIndex は 0 にリセットされます。
・それ以外の場合は、lastIndex は直近のマッチに続く次の位置にセットされます。

↑ここでは最後のルールに基づき、複数回実行されたtestメソッドでlastIndexの値が更新されていきます。
その後、最初のルールに基づき、lastIndexが文字列の長さよりも後ろにセットされ、testメソッドが失敗します。

var test_reg = new RegExp('.3' , 'g'); // 1回目 console.log(test_reg.test("4.3.3")); //true console.log(test_reg.lastIndex); // 3 // 2回目 console.log(test_reg.test("4.3.3")); //true console.log(test_reg.lastIndex); // 5 // 3回目 console.log(test_reg.test("4.3.3")); //false console.log(test_reg.lastIndex); // 0 <- 最初のルールが適用される

投稿2018/05/15 07:48

tkturbo

総合スコア5572

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yashiking

2018/05/15 08:01

tkturboさん、回答ありがとうございます。 'g'の設定がlastIndexを作成してしまってそれが原因で、連続で使用した時に検索し始める位置が変わってしまい思うような結果が出なかったということですか... 文字列内で同じ文字を複数個発見する必要はないので、(toLocaleLowerCase()を無くすためにも)flagはiにすることで解決させたいと思います。 勉強になりました!本当にありがとうございます!
guest

0

MDNに、気になる記述がありました。

test() が同じグローバル正規表現インスタンスで複数回呼び出されると、前回のマッチの先に進むことになります。

同じ正規表現で同じ引数にtestを繰り返し呼ぶと、想像していなかった動作になる模様です。

投稿2018/05/15 07:57

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yashiking

2018/05/15 08:05

maisumakunさん、回答ありがとうございます。 tkturboさんの回答でグローバルサーチが問題と分かったのですが、test()にもそのようなルールがあるのですね... 勉強になります!ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問