Power Automateで「{{name}} 様」のようなテンプレートに値を差し込んでメールを送りたい、と思ったことはありませんか?よくあるやり方だと、変数を用意して Apply to each で1件ずつ置換していくことになりますが、これ、項目が増えてくると意外と遅くて困ってしまうんですよね。
実は、変数もループも一切使わずに、しかもほぼ0秒でテンプレート置換ができる方法があります。今回紹介するのは「split を2回使う」というちょっと変わったテクニック。仕組みさえわかればとてもシンプルなので、順番に見ていきましょう。
やりたいこと
まずはゴールを整理しておきます。こんな穴あきテンプレートを用意して、
{{name}} 様
いつもお世話になっております。
{{期限}}までに下記のURLよりお手続きをお願いいたします。
{{url}}
ご不明な点がございましたらご返信ください。
よろしくお願いいたします。
{{name}} や {{url}} の部分を、対応表(辞書)に従って差し替えたい、というのが今回のテーマです。
name → 山田太郎
url → https://example.com/portal/abc123
普通に考えると「変数を使ってループで置換」となりますが、ここをあえて使わずにやってみます。
最終的には以下のようなフローになります。

なぜループや変数を避けるのか
ここで重要なのは、Apply to each は1件ずつ順番に処理するため、差し込む項目や対象データが増えるほど時間がかかってしまう、という点です。数件なら気になりませんが、数百件のメールを一括で処理するような場面では、この差がじわじわ効いてきます。アクションの実行回数も増えますしね。
そんな時に便利なのが、配列操作で一気に処理してしまう考え方です。split をうまく使うと、ループを回さずに置換が実現できてしまうんですね。
仕組み:なぜsplitを2回するのか
実装前に仕組みを整理します。このテクニックの肝は、その名のとおり split を2回かけるところにあります。ここからは置き換え文字のことを「トークン」と説明していきます。
1回目:{{ で分割する
まず、テンプレート文字列を {{ で分割します。すると、こんな配列になります。
["", "name}} 様 …", "url}} …"]
ここで注目したいのは、分割後の各かたまりが「トークン名 + }} + 後ろの文章」という形になっていることです(先頭だけは例外で、トークンを含まない素のテキストになります)。
2回目:それぞれを }} で分割する
次に、各かたまりをさらに }} で分割します。
"" → [""] ← 要素1個
"name}} 様 …" → ["name", " 様 …"] ← 要素2個
"url}} …" → ["url", " …"] ← 要素2個
ここがポイント:要素の数で見分ける
ここで気をつけたいのが、2回目の分割で要素がいくつになったかです。これを見るだけで、そのかたまりがトークンかどうかを判定できます。
- 要素が2個なら、1番目がトークン名、残りが後ろの文章
- 要素が1個なら、ただの素のテキストなのでそのまま返す
つまり、要素2個のものだけ「1番目の要素で辞書を引いて値に差し替え、後ろの文章をくっつける」という処理をすればいいわけです。条件分岐ひとつで済んでしまうのが、この方法のうまいところですね。
実際のアクション構成
では、実際にPower Automateで組んでみましょう。今回は3つのアクションで完成します。テンプレート文は テンプレート、辞書は 辞書 という Compose に入っている前提で進めます。
テンプレートには以下のようなメールテンプレートを設定しています。
{{name}} 様
いつもお世話になっております。
{{期限}}までに下記のURLよりお手続きをお願いいたします。
{{url}}
ご不明な点がございましたらご返信ください。
よろしくお願いいたします。
辞書はこんなJSONを入れておきます。
{
"name": "山田太郎",
"url": "https://example.com/portal/abc123"
}


1つ目:Select(splitを2回)
1つ目の Select アクションで、2段階の分割をまとめて行います。
From(分割元の配列を作る)には、以下のように記述します。
split(outputs('テンプレート'), '{{')
Map(各かたまりをさらに分割)には、こう書きます。
split(item(), '}}')
これで、出力は「配列の配列」になります。

実行結果

2つ目:Select(辞書で置換)
2つ目の Select で、辞書を引いて置換します。From には1つ目のSelectの出力を指定し、Map には次の式を入れます。

if(
greater(length(item()), 1),
concat(
coalesce(outputs('辞書')?[item()[0]], item()[0]),
join(skip(item(), 1), '}}')
),
item()[0]
)

少しだけ読み解いておきましょう。greater(length(item()), 1) でトークンかどうかを判定し、トークンなら item()[0](トークン名)で辞書を引きます。coalesce を使っているので、辞書に値がなければ元のトークン名をそのまま残します。そこに後ろの文章を concat で連結する、という流れです。
ここで気をつけたいのは、後ろの文章を item()[1] ではなく join(skip(item(), 1), '}}') で取っている点です。こうしておくと、万が一本文に }} という文字が紛れていても文章が欠けません。要素がちょうど2個のときは item()[1] とまったく同じ結果になるので、こちらの書き方にしておくと安心ですね。
実行するとこのような形になります。

3つ目:Join(つなげる)
最後に、2つ目のSelectの結果を Join アクションで連結します。Join with には何も入れず、空文字にしておきます。

join(body('選択_1'),'')
このJoinの出力を、そのままメール送信アクションの本文に渡せば完成です。

テストから実行すると正しく置換できていることが確認できます。


全体の流れ
実際に動かしてみると、文字列がこんなふうに変化していきます。
テンプレート文字列
↓ ① split('{{') → split('}}')
[ [""], ["name", " 様 …"], ["url", " …"] ]
↓ ② if で辞書引き&連結
[ "", "山田太郎 様 …", "https://… …" ]
↓ ③ join('')
完成したメール本文
①で「形を整える」、②で「中身を差し替える」、③で「元に戻す」という流れです。
使うときの注意点
いくつか気をつけたい点があります。
ただし、トークンは {{name}} のように詰めて書いてください。{{ name }} のように空白を入れてしまうと、辞書のキーに空白が混ざってうまく引けなくなってしまいます。
また、大文字小文字を区別したくない場合は、辞書のキーを小文字でそろえておき、item()[0] を toLower(item()[0]) で包んであげるとよいでしょう。
なお、トークンの数に上限はありません。2個でも10個でも、{{ で分割されたかたまりが増えるだけなので、同じロジックでそのまま処理できます。差し込み項目が増えても速度がほとんど変わらないのは、ループを使っていないこの方法ならではの強みですね。
また、今回は {{}}ですが、【】とかでもスペースを挟まないのであれば可能です。もちろん式の対象となる個所は適宜置き換える必要があります。
まとめ
いかがでしたでしょうか。「split を2回かけて、要素数でトークンを見分ける」というシンプルな発想で、変数もループも使わない軽量なテンプレートエンジンが作れるようになりました。
Apply to each で1件ずつ回すやり方に比べて圧倒的に速いので、差し込みメールや動的なドキュメント生成など、活躍する場面はたくさんあります。ぜひ一度、ご自身のフローで試してみてください。参考になれば幸いです。


コメント