JavaScriptで文字列テンプレート
- JavaScript linq.js - 09.05/14
以前のString.Formatモドきが散々だったので、今回はもっとマトモに真面目にlinq.jsを使って書きます。
// {}で囲まれたものを置換する var template = "食べ物は{food}で{dummy}飲み物は{drink}らしい。"; // このオブジェクトに対応したものに置換、という簡易テンプレート var dict = { food: "タコ焼き", drink: "コーラ" }; // 目標とする結果は「食べ物はタコ焼きで{dummy}飲み物はコーラらしい。」 // dummyが引っかかってしまうのでダメ var text = template.replace(/\{(.+?)\}/g, function(m, c) { return dict[c] });
普通に横着して.+?で置換しようとすると、{dummy}もマッチしてしまって宜しくない。別にひっかかってもいいじゃん、オブジェクトで指定しているのしか{}で囲まないよ!とばかりも言ってられないシチュエーションは、それなりにある、と、思う。というわけでちゃんとオブジェクトのキーだけを抜き取って正規表現を生成する。
// テンプレートに使うキーだけを抜き取る var key = E.From(dict).Select("$.Key").ToString("|"); // food|drink var regex = new RegExp("\{(" + key + ")\}", "g"); var text = template.replace(regex, function(m, c) { return dict[c] });
linq.jsならSelectしてToStringするだけで出来あがる。と、いうわけで簡単です。ついでなので汎用的な関数にしてみる。区切りは決め打ちで{}です。気に入らなければ%%でも${}でも好きに変更してください。
// テンプレートで置換・区切り文字はとりあえず{} function templateReplace(template, replacement) { var key = E.From(replacement).Select("$.Key").ToString("|"); var regex = new RegExp("\{(" + key + ")\}", "g"); return template.replace(regex, function(m, c) { return replacement[c] }); } var template = "食べ物は{food}で{dummy}飲み物は{drink}らしい。"; var text = templateReplace(template, { food: "タコ焼き", drink: "コーラ" });
しかし毎回オブジェクトを作るってのも面倒くさい。順番決め打ちで気楽に置換したい時って沢山あると思う。というわけで、オブジェクト以外を渡した時はC#のstring.Formatと同じ動作をするように変更。typeofとかinstanceofとか、JavaScriptって面倒くさいよね。
// replacementがオブジェクトでない場合は可変長引数として動かす function templateReplace(template, replacement /* args */) { var key; if (typeof replacement != "object") { key = E.Range(0, arguments.length - 1).ToString("|"); replacement = E.From(arguments).Skip(1).ToArray(); } else if (replacement instanceof Array) { key = E.Range(0, replacement.length).ToString("|"); } else { key = E.From(replacement).Select("$.Key").ToString("|"); } var regex = new RegExp("\{(" + key + ")\}", "g"); return template.replace(regex, function(m, c) { return replacement[c] }); } // 名前をつけてObjectを渡す形式でも var template = "食べ物は{food}で{dummy}飲み物は{drink}らしい。"; var text = templateReplace(template, { food: "タコ焼き", drink: "コーラ" }); // 数字で指定する形式でも、どちらでも動く var template = "食べ物は{0}で{dummy}飲み物は{1}らしい。"; var text = templateReplace(template, "タコ焼き", "コーラ"); var array = ["タコ焼き", "コーラ"] var text = templateReplace(template, array); // 配列で渡しても平気
argumentsをFrom.Skip.ToArrayと、サクサクーとメソッド繋げて変換出来るので楽ちん。
C#でも大体同じ感じで書けます。ToStringは、String.Joinで代用可能。置換部分は、Regex.Replace(template, “正規表現”, m => replacement[m.Groups[1].Value]);ですね。replacementはDictionary<string, string>で。ラムダ式が便利なので、MatchEvaluatorがようやくホイホイ使えて、JavaScript並みに気楽に正規表現を書けるようになりました。
と、いうことで
試してみてくれるとかDisってくれるとか、はてブしてくれるとか紹介してくれるとか切望中。