Power Automate で key-value Array ⇔ Object (Dictionary) を相互変換して Office Script の呼び出しを節約する

本記事は、 Microsoft Power Automate - Qiita Advent Calendar 2024 - Qiita シリーズ3 の 21日目 の記事だ。
…本当は、 シリーズ1 の 19日目 に投稿しようとしてたのだけれど、諸事情で記事更新ができず、代理投稿されてしまったので、改めて投稿し直している。

今回は、 Power Automate に於いて、連想配列の形で情報を持たせた Object と、 key-value Array の間を相互変換する方法を解説する。

Premium コネクタやループ処理などは使用せず、 O(1) オーダーのなるべく少ないステップ数で Object ⇔ Array 配列の相互変換させよう。

昨年の Power Automate Advent Calendar 2023 と同様、ステップ数を節約するテクニックの話題だ。

key-value Array ⇔ Object の変換目的

Array から Object に変換すると、 Power Automate の数式で variables('val-name')['property-name'] とアクセスできる。
このため、ループ処理で一旦統計したデータを後から参照させる場合などで都合が良い。

一方で Object から Array にしておくと、 選択アクション を使って 1ステップ で反復処理が行えるため、処理ステップ数を節約できる。

ほか活躍できる具体例として、Array にすることで Office Script との間での辞書型の情報の受け渡しが可能になることが挙げられる。

Office Script との引数・戻り値の受け渡しには key-value Array がよい

スクリプトのユーザー入力を取得する - Office Scripts#入力の制限 にあるように、 Office Script の入出力に設定する『型』は、リテラル型とそれらによる配列・オブジェクト型に限られる。
TypeScript で言う Indexシグネチャで表現できるディクショナリパターン の型を、 Office Script の main 関数のパラメータに使おうとすると、 Type literal contains invalid type 'Unrecognized' エラーになってしまうのだ。 1

function main(
  workbook: ExcelScript.Workbook,
  arg1: { [key: string]: { prop1: string, prop2: string } }
) { // <- Parameter type error for parameter 'arg1: TypeLiteral': Type literal contains invalid type 'Unrecognized'.
    const val: { [key: string]: { prop1: string, prop2: string } } = {}; // <- ok
}

幸い引数に object 型を設定することはできるが、型情報が綺麗サッパリ消えてしまうので、型アサーションに頼る必要が出てくる。
加えて、それでも返り値には object 型を設定できない。

function main(
  workbook: ExcelScript.Workbook,
  arg1: object
) {
  for (const key of Object.keys(arg1)) {
    const t = arg1[key] as { prop1: string, prop2: string };
  }
  return arg1; // <- Return type error: 'Object' is not supported. Try using an interface or type literal instead
}

つまり、 HTTP 要求トリガーbody('HTTP_要求').inputs.headers のような、任意のプロパティ名のオブジェクトで渡されるような情報を、 Office Script で処理して返しづらい。

このため代わりに、 { key: string, value: TYPE }[] な key-value 配列に変換して受け渡すことで、型情報を維持したまま Office Script との間で辞書型の情報を受け渡せるわけだ。

function main(
  workbook: ExcelScript.Workbook,
  arg1: { key: string, value: { prop1: string, prop2: string } }[]
): { key: string, value: { prop1: string, prop2: string } }[] { // <- ok
  return arg1; // <- ok
}

Power Automate からの Office Script の呼び出しには 3回/10秒 という制限があるので、 Power Automate の同一フロー中に何度も呼び出したり、ましてのフロー内のループ中に呼び出す事はできない。
このため、 key-value Array を引数に Office Script を 1回 呼び出してその中でまとめて処理をし、 Power Automate 側で key-value Array から辞書型のオブジェクトに戻すのが良いだろう。

相互変換の実装

続きを読む

Power Automate で少ないステップ数でコレクションを操作するコツ

本記事は、 Microsoft Power Automate Advent Calendar 2023 - シリーズ2 の5日目の記事だ。

"シリーズ1" の4日目は、 @inaho3517 氏の 本でPower Automateを勉強してみた だ。
ちょっとAmazonで検索しただけでも、 めっちゃ本が出てる のね…
知らなかった。
シリーズ2はまだまだ空きがあるので、ぜひご参加を。


実行ステップ数を節約したい

さて、 Power Automate を含め Microsoft Power Platform には、プラン毎に最大要求数の制限がある。
例えば、多くの人が使っているだろう Office 365, Microsoft 365 F3/E3/E5 に含まれる Power Automate の場合、 24時間ごとの 6,000 要求数が上限だ。

Power Automate では、実行されたトリガー・アクションひとつひとつが、結果の成否にかかわらずこの「要求数」を消費する。
大きな数のループや、2重ループなどを行うと、意外と簡単に 6,000 要求数に達してしまう。

時々妥当な範囲で上限を超える程度なら、即時実行をブロックされることはない ものの、設計の段階では超えない事に越したことはない。

唐突だが、アルゴリズムなどの計算量の効率を示す指標として、オーダー表記というものがある。

雑に説明すると、N件のデータを処理するのにどれくらいの勢いで計算コストが増えていくかと言ったもので、例えば単純なループ処理なら O(N), 2重ループとかになると O(N2) といった書き方をするのを、一度は目にしたことがあるのではないだろうか?

ということで、ループによってアクション要求数(ステップ数)が増えがちな Power Automate のコレクションの処理を例に、ループ処理を排除し O(1)定数時間で処理する方法を紹介する。

とりあえず Office Script?

続きを読む

Power Automate の式で property doesn’t exist となるのを解消する

Power Automate (旧 Microsoft Flow) で式を記述して実行した時、以下のようなエラーに遭遇することがある。

The template language expression '*' cannot be evaluated because property '*' doesn't exist, ...

特によく遭遇するのが、 HTTP トリガーでヘッダーを参照するときだ。

例えば、 Slack のイベント購読を HTTP トリガーで受ける時に、

triggerOutputs()['headers']['X-Slack-Retry-Reason']

のようにしてヘッダーの情報を参照しようとすると、 このヘッダが存在しない初回の実行時に上述のエラーが発生してしまう。

contains(triggerOutputs()['headers'], 'X-Slack-Retry-Reason') のように、プロパティの有無のチェックの and 論理演算を加える方法 などが紹介されているが、実はもっと簡単な方法がある。

それは、 疑問符演算子 "?" を使うことだ。
(C# で言う Null 条件演算子 のようなもの)

これを使って、

triggerOutputs()['headers']?['X-Slack-Retry-Reason']

と記述すれば、

if(contains(triggerOutputs()['headers'], 'X-Slack-Retry-Reason'), triggerOutputs()['headers']['X-Slack-Retry-Reason'], null)

と等価になる。

各プロパティがオプショナルなものかどうかは、ヘルプを見ればある程度わかるものだが、メンバーアクセス演算子やインデクサー演算子を使う場合は、常に疑問符演算子を併せて使うのがよいのかもしれない。
Power Automate のドキュメント内の "条件付きの式を使用する" などのサンプルでも、常に疑問符演算子がつけられているので。


Power Automate のバックエンドは、 Azure Logic Apps となっているらしく、 式で使える 演算子式関数 、そして 組み込みのトリガー などに関するリファレンスが、 Azure Logic Apps のドキュメントにしか存在しない。

また、 コネクタのリファレンス も、 Power Automate のドキュメントとは別になっている。

同じ情報が二重三重に書かれて内容に同期が取れないような状態になるよりはマシだが、欲しい情報が探しにくいのが辛いところだ。。。