JavaScriptで文字列テンプレート
- 2009-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ってくれるとか、はてブしてくれるとか紹介してくれるとか切望中。