主語を入れ替えるか目的語を入れ替えるか

ちょっとしたスクリプトで、"+foo" という文字列を "OKs" という配列に、"-bar" という文字列を "NGs" という配列に入れる、という必要が生じました。

var OKs = [], NGs = [];
"+foo -bar +baz ...".replace(/([-+])(\S+)/g, function(_, pm, value) {
  if (pm == "+")
    OKs.push(value);
  else
    NGs.push(value);
});

こういうのは出来れば一行で書きたいですよね? ということで幾つかバリエーションを考えてみました。

ふつうの三項演算:

pm == "+" ? OKs.push(value) : NGs.push(value)

push(value) が共通の操作であることに気付きます。こんな場合は:

(pm == "+" ? OKs : NGs).push(value)

と、括弧でメソッドの動作主体 (主語) をまとめてしまうことが出来ます。

逆のやり方もあります (と言うか先にこっちを思いつきました):

Array.prototype.push.call(pm == "+" ? OKs : NGs, value)
// あるいは
[].push.call(pm == "+" ? OKs : NGs, value)

この場合、メソッドの動作主体は Array クラスまたは任意のインスタンスであり、その適用対象 (間接目的語) をどちらにするかを選択している、と捉えることができます。

どっちの発想が自然なのかな? ということをふと思ったもので、書いてみました。


ちなみに、スクリプトというのはこういうものです:

register_command("!|filter", function() {
  var OKs = [], NGs = [];
  Array.from(arguments).join(" ").replace(/([-+])(\S+)/g,
    function(_, pm, folder) {
      Array.prototype.push.call(pm == "+" ? OKs : NGs, folder);
    });
  if (!(OKs.length || NGs.length)) return;

  subs.model.load(subs.model.list.filter(function(v) {
    return NGs.length ? !NGs.some(function(x){return v.folder.match(x)})
                      :  OKs.some(function(x){return v.folder.match(x)});
  }));
  subs.update();

  var joinp = function(ary, sep) { return sep+ary.join(sep) };
  message([
    "Filtered subs:",
    joinp.apply(null, NGs.length ? [NGs," -"] : [OKs," +"])
  ].join(""));
});

livedoor Reader で、フィード一覧のツリー構造を保ったままフォルダ単位で絞込みをする、というものです。主にレート表示での利用を念頭に置いています。

例えば今は趣味系は消したいとか、技術系だけ表示したい、みたいなことが出来ます。

これまでレートの中に色んなフィードが混在するのを避けようとして、重要度よりも内容でレート付けする (ニュース系は 5、技術系は 4 みたいに) という、間違った使い方をしていたんですが、それを何とかするために作ってみました。

vi コマンドで ":! -hobby" とすると "hobby" というフォルダのフィードだけが消えます。逆に "+" を前置するとそのフォルダだけが表示されます。

条件を複数指定することも可能ですが、"+" と "-" を混在させることは出来ません ("-" の条件だけが有効になります)。

私みたいにフィードをきっちりフォルダ管理している人にしか意味無いですが、他のフィルター方法を色々と考えてみるのも面白いかも知れません。


追記 [20061011]:

三項演算子の代わりに "&&" と "||" の組み合わせでも OK ですね。

例:

Array.prototype.push.call(pm == "+" && OKs || NGs, folder);

pm の値が "+" であれば OKs が、そうでなければ NGs が論理式の値となります (感覚的にブール値になると思ってしまいがちかも知れませんが、そうではないということです)。

ただし、"&&" の次に来るものが「真」と評価されるかどうかに注意する必要があります。例えば、もし空の配列が「偽」と評価されるなら (JavaScript では「真」ですが)、上の表現では正常に動作しないことになります (全て NGs に push されてしまう)。