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

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

ただいまの
回答率

90.50%

  • JavaScript

    16440questions

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

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

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 116

yashiking

score 2

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させようとしています。

var dotfour = ['4.4.1','4.4.2','4.4.3'];
var dotthree = ['4.3.1','4.3.2','4.3.3'];
var four = ['4.1','4.2',dotthree,dotfour,'4.5'];
var array = ['1','2','3',four,'5'];

function test_list(array,loop) {
    var test_reg = new RegExp('.3' , 'g');
    array.forEach(function (list,index) {
        console.log(list,loop);
        if (typeof list === "object"){
            if (loop === 1){
                test_list(list,loop + 1);
            }else if(loop === 2){
                test_list(list,loop +1);
            }
        }else if(typeof list === "string"){
            console.log(list + 'の時、if前は' + test_reg.test(list.toLocaleLowerCase()));
            if (test_reg.test(list.toLocaleLowerCase())){
                console.log(list + 'の時、if後は' + test_reg.test(list.toLocaleLowerCase()));
            }
        }
    });
    if (loop === 1){
        console.log('end');
    }
}
$(function () {
    test_list(array,1);
});

しかし、結果は、

1 1
1の時、if前はfalse
2 1
2の時、if前はfalse
3 1
3の時、if前はfalse
(5) ["4.1", "4.2", Array(3), Array(3), "4.5"] 1
4.1 2
4.1の時、if前はfalse
4.2 2
4.2の時、if前はfalse
(3) ["4.3.1", "4.3.2", "4.3.3"] 2
4.3.1 3
4.3.1の時、if前はtrue
4.3.2 3
4.3.2の時、if前はtrue
4.3.3 3
4.3.3の時、if前はtrue
4.3.3の時、if後はfalse
(3) ["4.4.1", "4.4.2", "4.4.3"] 2
4.4.1 3
4.4.1の時、if前はfalse
4.4.2 3
4.4.2の時、if前はfalse
4.4.3 3
4.4.3の時、if前はtrue
4.5 2
4.5の時、if前はfalse
5 1
5の時、if前はfalse
end

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

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

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

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

4.3.1 3
4.3.1の時、if前はtrue
4.3.1の時、if後はfalse
4.3.2 3
4.3.2の時、if前はtrue
4.3.2の時、if後はfalse
4.3.3 3
4.3.3の時、if前はtrue
4.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後で参照している数字が変わっていない)ので、そうではないと思います。

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

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 17:01

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

    キャンセル

+1

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/15 17:05

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

    キャンセル

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

  • ただいまの回答率 90.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • JavaScript

    16440questions

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