ぶれすとつーる

だいたいjavascript

単語前方一致・単語後方一致・単語完全一致 String.indexOfの魔力

indexOfにはあまり知られてない裏技的な使い方がある。


よく知られてるString.indexOf

indexOf

構文
indexOf(searchValue[, fromIndex])

パラメータ

searchValue
検索する値を表す文字列。

fromIndex
呼び出す文字列内の検索を始めるための位置。0 とその文字列の長さの間にある整数を指定できます。デフォルトの値は 0 です。

概要
呼び出す String オブジェクト 中で、指定された値が最初に現れたインデックスを返します。fromIndex から検索を始め、値が見つけられない場合、-1 を返します。

indexOf - MDC Doc Center


概要からわかるように、文字列の出現インデックスを取るものだが、肝心なのは見つからない場合-1を返すことだ。

これを利用してとある条件の下で以下の三つの正規表現ライクなことが実装できる。

  1. 単語前方一致
  2. 単語後方一致
  3. 単語完全一致 ← これ重要


条件は簡単、検索対象の文字列中がスペースが含まれない『単語』であること。

まずはそれほど実用性のない二つ(ならのせなくていいとかいわないで。

単語前方一致

var word   = "interesting",
    target = "int";

var str = " " + word;
if (str.indexOf(" " + target) !== -1) {
    alert("単語前方一致TRUE");
}


// 単語前方一致TRUE

変数word内のinterestingがintから始まる文字列かどうかのチェック
原文とキーワードの両方の先頭に空白をつけることで文頭のマッチングが判断できる。



単語後方一致

var word   = "interesting",
    target = "ing";

var str = word + " ";
if (str.indexOf(target + " ") !== -1) {
    alert("単語後方一致TRUE");
}


// 単語後方一致TRUE

単語前方一致と同様の考えで末尾にスペースをつけて後方判定


そして最後は単語完全一致


単語完全一致

var word   = "interesting",
    target = "interesting";

var str = " " + word + " ";
if (str.indexOf(" " + target + " ") !== -1) {
    alert("単語完全一致TRUE");
}

先頭と末尾にそれぞれスペースをいれて完全一致を判定する。



ん? ちょっとまて。

それなら ("interesting" === target) の評価でいいじゃん!!


ってなってしまいますけどこれではできないindexOf版単語完全一致の用途があるのです。


たとえば単語をスペース区切りで陳列した以下のような文字列があったとする。


"hoge fuga foo piyo"


この中にfugaという文字列かあるかどうかは単純な文字列比較で表現できないが、先ほどの単語完全一致を使えばうまく判定できる。

var word   = "hoge fuga foo piyo",
    target = "fuga";

var str = " " + word +" ";
if (str.indexOf(" " + target +" ") !== -1) {
    alert("単語完全一致TRUE");
}
// 単語完全一致TRUE

こんな感じに実装すればOK



なんとなく実用性ありそう・・・だけどどこで使えばいいかなって疑問がわくと思います。

例でいうと非互換関数のdocument.getElementsByClassNameとかのIE互換化の処理とかで活躍します。
classの値は以下のように複数設定が可能なのでばっちり今回のケースでなんとかなります。

  • class="fuga piyo hoge"
document.getElementsByClassName = function (needle) {
    var allNodes = document.getElementsByTagName('*'),
        nodes    = Array.prototype.slice.call(allNodes),
        outArray = [],
        i,c,l;
    
    for (i = 0, l = nodes.length;  i < l; i++) {
        if (nodes[i].hasAttribute("class")) {
            c = " " + nodes[i].className + " ";
            if (c.indexOf(" " + needle + " ") !== -1) {
                outArray.push(nodes[i]);
            }
        }
    }
    return outArray;
};

document.getElementsByClassName('fuga');

さらに前方単語一致を使って fuga1 , fuga2 , fuga3 といったprefixが統一されてるクラスだけを抜き取ったり後方単語一致を使ってその逆も実装できたりします。

便利ですね。 indexOf