LoginSignup
15
13

More than 5 years have passed since last update.

JavaScriptでAND/OR/NOT条件を使ったテキスト検索

Last updated at Posted at 2015-03-28

実行サンプル

var condition = [
  ['dog', 'fox', '-cow'],
  ['cat'],
  ['pig'],
]; // 意味は (dog AND fox AND !cow) OR cat OR pig

var matcher = compile(condition);

matcher.test("The quick brown fox jumps over the lazy dog.")); // true

compile関数の中身

function compile(cond) {
  var joinAnd = function (arr) { return '^(?=[\\s\\S]*' + arr.join(')(?=[\\s\\S]*') + ')'; };
  var joinOr  = function (arr) { return '(?:' + arr.join('|') + ')'; };
  var escape  = function (str) { return str.replace(/(?=[(){}\[\].*\\^$?])/, '\\'); };
  var rx = joinOr(cond.map(function(inner) { return joinAnd(inner.map(escape)) }));
  rx = rx.replace(/=\[\\s\\S\]\*-/g, '![\\s\\S]*');
  return new RegExp(rx);
}

解説・補足

  • 基本的に以下のような意味の正規表現を作ってるだけです。
/(^(?=.*dog)(?=.*fox)(?!.*cow)|cat|pig)/
  • (?=) で先読みしているためAND条件部分はどれが先に来てもマッチできます。
  • 先読みの中に .* が入っており対象文字列の先頭で1回試行してゼロ幅マッチしてくれれば良いようにしているため ^ を置いています。これを忘れると対象がN文字だった時N倍の時間がかかります。
  • 正規表現中の . は改行にマッチしないので [\s\S] で代替しています。
  • 当然ですが行っているescape処理を省けば条件に正規表現が使えます。
  • 特定文字の前に「\」を置くだけのエスケープなら先読みを使うことで、キャプチャせず単純なreplaceで表現できます。
  • new RegExp(rx, 'i'); にすれば大文字小文字を無視します。
  • 適当にNode.jsでベンチマークをとった所、コンパイル部分のコストを無視しても、ループして地味に indexOf する方が倍くらい早かったです。
  • これを書いている時にNOT機能を思いついて後付けしています。

使い所

コードが少ない割に十分な機能と小回りが効くようにしてあるため、ユーザスクリプトに組み込んで使っています。
こんな感じで、見たくないコメントなどをあぼーんしたりするのに使えます。
(ユーザスクリプトで使う場合エスケープ処理は無くしてしまった方が使いやすそうです)

Array.prototype.slice.call(document.getElementsByClassName('comment'))
    .filter(function(c) { return matcher.test(c.textContent) })
    .map(function(c) { c.className += ' hide'; });

逆にGoogle検索で (A|B) C D とするように、複数の対象の中から目的のものを探し出すような使い方をする場合、joinAndjoinOrの呼び出している部分を交換します。
(恐らくタイトルから連想される動きはこちらだと思います)

  var rx = joinAnd(cond.map(function(inner) { return joinOr(inner.map(escape)) }));
15
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
13