ルドルフもわたるふもいろいろあってな

Microsoft 365、Power Platform、PowerShellについて調べたことや検証したことなどを投稿します。技術の話は面白い。

【解説編】【改良版】ExcelのテーブルをClearCollect関数の実行文に変換する

改良版フローの解説をします。

経緯

今回は概要編改良版フローの説明の続きです。

解説の概要

このフローの目的は「ExcelのテーブルをClearCollect関数の実行文に変換する」ことです。

そして、このフローの特長として挙げられることは「テーブルに存在するすべての列の型を確認して、日付形式のデータが存在する場合は指定書式(例:yyyy/M/d)に変換する」ことです。

ポイントを挙げて解説します。

解説のポイント

解説のポイントは以下の3つです。

  • 配列のプロパティに対して繰り返し処理を行う
  • 日付型であるかどうかの判定
  • フローのエラーハンドリングに関するTIPS

ポイント1:配列のプロパティに対して繰り返し処理を行う

ポイントのひとつめです。

配列が持つ各プロパティが日付型であるかどうかを判定するために、まずはプロパティごとに判定を行うための準備をします。

プロパティごとに判定を行う」ということはつまり、プロパティの個数だけ繰り返し処理を行うということです。

今回のサンプルとして使用しているExcelのテーブルは4つの列をもっています。つまり、配列が持つプロパティの個数は4個です。

  • 書籍名
  • 著者名
  • 出版日
  • 価格

さあ、では。配列のプロパティごとに繰り返し処理を行うにはどうしたらいいか???

このフローでは「データ操作」>「選択」アクションを使って"元の配列"から"プロパティ名の配列"を作成し、その"プロパティ名の配列"を繰り返し処理のインデクサ(= 配列の各要素にアクセスするための索引)として使用しています。


具体的に解説します。

まずは、下図が「選択」アクションの入力となる配列です。この配列から「選択」アクションを使ってプロパティ名だけを抜き取ります。

これがその「選択」アクションです。

「開始」欄と「マップ」欄に記載している式はそれぞれ以下の通りです。


「開始」欄に記載している式:

"from": "@split(substring(string(items('Apply_to_each-配列の要素ごとに繰り返し処理')), 1, sub(length(string(items('Apply_to_each-配列の要素ごとに繰り返し処理'))), 2)), ',')",

「マップ」欄に記載している式:

"select": "@substring(substring(item(), 0, indexOf(item(), ':')), 1, sub(length(substring(item(), 0, indexOf(item(), ':'))), 2))",

「選択」アクションの「"開始"欄に記載している式」の結果

「開始」欄に記載している式は元の配列の括弧({})を削除たうえで、カンマを区切り文字として文字列を分割しています。

「開始」欄に記載している式:

"from": "@split(substring(string(items('Apply_to_each-配列の要素ごとに繰り返し処理')), 1, sub(length(string(items('Apply_to_each-配列の要素ごとに繰り返し処理'))), 2)), ',')",

この式の結果は以下の通りです。

ポイントは2つです。

  • split関数によって文字列を分割して配列に変換したことによって、配列を表す括弧( [ ] )が自動で付加された
  • 配列の要素の前後を囲むダブルクォーテーションと区別するために、要素内で使われているダブルクォーテーションにエスケープ文字としてのバックスラッシュが自動で付加された
「選択」アクションの「"マップ"欄に記載している式」の結果

「マップ」欄に記載している式は「開始」欄の式が返した文字列のコロンより左側を取得したうえで前後のダブルクォーテーションを取り除いています。

「マップ」欄に記載している式:

"select": "@substring(substring(item(), 0, indexOf(item(), ':')), 1, sub(length(substring(item(), 0, indexOf(item(), ':'))), 2))",


この「選択」アクションで得られる出力が以下の配列です。期待通り「プロパティ名の配列」を得ることができました。


この「プロパティ名の配列」を後続のアクションである繰り返し処理のインデクサとして使用します。

ポイント2:日付型であるかどうかの判定

ポイント1で取得した「プロパティ名の配列」をインデクサとして繰り返し処理を行います。

サンプルのExcelテーブルは列が4つであるため、この繰り返し処理のループ回数は4回です。

1回のループごとに「そのプロパティが日付列であるかどうかの判定」と「指定した日付フォーマットへの変換」を行います。


まずは、そのプロパティの値を取得します。

items('Apply_to_each-配列の要素ごとに繰り返し処理')?[items('Apply_to_each-プロパティごとの繰り返し処理')]


次に、そのプロパティの値に対してconvertTimeZone関数を実行します。この関数によって対象の値が指定した日付フォーマットに変更されます。
この関数は引数の値が日付形式であれば成功しますが、日付形式でないときはエラーを返します。ここがポイントです。

convertTimeZone(outputs('作成-プロパティの値を取得'), 'UTC', 'Tokyo Standard Time', 'yyyy/M/d')


次に、先ほどのconvertTimeZone関数の成否を取得します。ここで使うのはResult関数です。Result関数は For_each、Until、Scope 内のアクションの実行結果を取得する関数です。この関数を使って、先ほど実行したconvertTimeZone関数の成否(Status)を取得します。

first(result('スコープ-取得したプロパティに対してconvertTimeZone関数を実行'))?['status']


ちなみにResult関数の出力は以下の通りです。出力のプロパティのひとつにアクションの成否(Status)があります。また、Result関数の結果は配列であるためfirst関数を使って要素を取り出しています。


そして、Result関数の結果が成功(Succeeded)であるかどうかで処理を分岐します。


処理が成功(Succeeded)であれば setProperty関数を使って配列の値を更新します。つまり、プロパティの値が日付型であるときだけ日付フォーマットを変更した値で元の配列を更新するということです。

ポイント3:フローのエラーハンドリングに関するTIPS

注意:ここで解説する内容は経験則をもとにしています。解説の内容が正確でない場合があります。

これはポイント2の捕捉にあたります。

下図のようにフローのアクションでエラーが発生しています。このエラーは日付型ではないプロパティに対してconvertTimeZone関数を実行したことによるエラーです。アクションでエラーが発生していますがフローとしては"成功"というステータスで終了しています。それはなぜでしょうか?

その理由は、フローの成否(成功or失敗)はそのフローの最後に実行されたアクションの成否(成功or失敗)で判定しているためです。

下図の通り、エラーが発生しているスコープの次のアクションとして「作成-スコープ内の処理成否」というアクションを配置しています。

「作成-スコープ内の処理成否」アクションは「実行条件の構成」設定によって直前のアクションの成否に関わらす実行されるようにしています。これによりconvertTimeZone関数を実行しているスコープがフローの最後に実行されるアクションなることを回避しているため、convertTimeZone関数が失敗してもフローの失敗になりません。

最後に

「convertTimeZone関数の成否によって日付列であるかを判定する」という手法を解説しました。この手法はアクションがエラーになることを正常系の処理に組み込むという手法です。

このようにアクションがエラーになることを前提としたフローはおすすめはしません。なぜなら、フローに例外処理をいれたときにそのエラーが正常なのか異常なのかがややこしくなるためです。

基本的にはフローの正常系はエラーを前提としないことをおすすめします。それではどうしても要件が実現できないときだけ、設計が複雑になることを念頭に置きながら使うようにしてください。

ちなみに

「関数が成功したかで型判定する」方法についてはコルネさんのブログでも解説されています。合わせて参照ください。よいまとめです。
koruneko.hatenablog.com


今回は以上です。