JavaScriptで文字列テンプレート

以前の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ってくれるとか、はてブしてくれるとか紹介してくれるとか切望中。

Profile

Yoshifumi Kawai

Cysharp, Inc
CEO/CTO

Microsoft MVP for Developer Technologies(C#)
April 2011
|
July 2024

Twitter:@neuecc GitHub:neuecc

Archive