Power Platformのイベントに参加した際、参加者の方からこんな相談を受けました。
「3つのファイルに書かれたデータから差分を取得したいんですけれど、うまくいかないんですよね。Power Automateでできたりするでしょうか?」
見せていただいた手書きの図はこんな感じでした。公開してもOKとのことだったので掲載します。
求めたいのは3つのファイルのうち、そのファイルに独自の項目。図でかくところの扇形の部分です。
たとえば、各ファイルに以下のようなデータが含まれていたとします。
ファイル名 | 値 |
---|---|
ファイルA | 兵庫,京都,大阪 |
ファイルB | 滋賀,奈良 |
ファイルC | 大阪,奈良,和歌山,三重 |
イメージが湧きやすいように、先程のベン図にマッピングしてみます。位置関係は気にしないでください。あくまでデータの例なので。
上記のようなデータの場合、取得したいのはファイルAに含まれていて、他の2つのデータには含まれない青い部分のデータなので、「兵庫」と「京都」になります。
操作1
考え方としては、ファイルA以外のデータ、つまりファイルBとファイルCのデータの中身をひとつの配列データにしてみます。
[大阪,滋賀,奈良,和歌山,三重]
Power AUtomate をつかって複数の配列から重複を除くには、union関数を使います。
このあたりの詳しい解説は、以前にブログに書いたことがあるので、よろしければ参考にしてください。
操作2
ファイルAのデータを、ひとつずつ操作1で作成できた配列の中に含まれるかどうかを判定し、含まれていたら無視。含まれていなければ変数に追加していきます。
これで、ファイルAにだけ存在するデータの配列を作成できるはずです。
実際の操作はPower Automate のクラウドフローを作りながら見ていきましょう。
Power Automate クラウドフローで処理を作る
Excelファイルのテーブルを用意する
今回のリクエストは、Excelファイルに書かれたデータということでしたので、3つのファイルにそれぞれテーブルを作成してデータを入力しました。
作成したファイルはSharePoint上に配置しておきます。
ファイルを配列化する
まず、先程作成したExcelファイルを、「表内に存在する行を一覧表示」アクションで取り込みます。
次に、「選択」アクションを作成し「開始」の部分には先程のExcelのアクションから動的なコンテンツの「value」を配置します。
次に、マップ部分は右の方にある「T」のようなボタンを押してモードを変更し、式として以下のように設定します。Excelのテーブルに設定したヘッダー名が異なる場合には適当に修正してください。
item()?['県名']
ファイルが用意できたら、その中身のデータをPower Automateで扱いやすいように配列の形にします。
配列を作るには「選択」アクションをたったこれだけで、Excelのテーブルを配列化することができます。
ファイルB、ファイルCも同じように配列化する
先程ファイルAを配列化できたので、ほかの2つも同じように処スコープを作ってコピーしましょう。
スコープは複数の処理をひとまとめにできる箱のようなものです。わかりやすいように名前を変更して、先程作った処理をスコープの中にドラッグアンドドロップします。
できたら、スコープの3点リーダーから「クリップボードにコピー」をクリックします。
コピーしたらExcelで一覧化するファイルを変更したり、スコープなどの名前も変更して区別しやすくしておきます。
次の処理でつかうのは、「選択」の部分なので、ここも区別しやすいように名前を変えておきました。
同じように、ファイルCのスコープも作成します。こまめにテスト実行してちゃんと指定したファイルの中身が配列化できているか確認しておきます。
操作1の実装
ファイルBとファイルCを重複のない(一意な、ユニークな)配列にします。この配列の受け皿として「作成」を作ります。「新しいステップ」をクリックしたあと、組み込みの中から「作成」を探してクリックします。
「式」モードで「union()」と入力したあと、「動的なコンテンツ」タブを選択して、「選択B」の「出力」をクリックします。
すると、unionのカッコの中に選択Bを式にしたものが自動入力されるので、そのあとにカンマ「,」を入力して、次に「選択C」の出力をクリックします。
この操作で何ができるかというと、以下のような式です。
union(body('選択B'),body('選択C'))
union関数の第1引数、第2引数に、ファイルBの配列とファイルCの配列を与えています。これによって2つの配列を合体させたうえで、重複のない配列が出来上がります。テスト実行してみます。
操作2の実装
ファイルBとファイルCの塊ができたので、あとはファイルAの値をひとつずつ配列の中に含まれるかどうかを判定していくだけです。
このあと Apply to each を使ったループ処理をおこないますが、実はもっと良い方法があります。
後半にて変更しているので、操作2の実装をする前に、こちらに目を通していただいたほうがよいかも。
含まれていないと判定された値をキープしておくために、あらかじめ変数の初期化をしておく必要があります。
変数の初期化は処理の先頭のほうでまとめておくのが良いと思うので、この位置に作りました。適当な名前をつけて、種類には必ず「アレイ」を選択します。初期値の部分は空でもかまいません。空の配列を表す角括弧のセットを入力しておいてもよいでしょう。
[]
「選択A」の中身を「作成」の配列に対してひとつずつチェックしていくためには、「それぞれに適用する」アクションを使います。
ひらくと、「Apply to each」というのがでてくるので、最初の欄には「選択A」の「出力」を動的なコンテンツから選んでクリックします。
さきほどファイルB、ファイルCから合成した「作成」というアクションの名前が紛らわしいので「BC配列」というように変更しておきました。変わりに、Apply to eachの中に「作成」を新たにつくりました。
この「作成」というアクションはテキストでも配列でも入れておけるので、結果を一時的に確認するようなときにとても重宝します。
「作成」のなかには、配列に含まれるかどうかを判定するための式を入力します。
contains関数の第1引数にはBC配列をいれます。第2引数に書いてある「item()」というのは、Apply to eachで順番にループするなかで取り出す値を表します。
Apply to eachの最初の欄に設定したのは「選択A」の配列なので、配列の1要素ずつを順番に第2引数にはめ込んで繰り返すような動きをしています。
ちょっと難しいのですが、item() を使いこなせるようになるとできることの幅が一気に広がります。
contains(outputs('BC配列'),item())
テスト実行すると、表示1/3 のように記載されています。これは3回ループが回ったことを示しています。なぜ3回なのか? それは、Apply to each に設定したのが選択Aだから。そしてその要素は「兵庫,京都,大阪」の3つでしたね?
「作成」の中身は 「false」となっています。1つ目の要素「兵庫」がBC配列の中に含まれるかチェックされた結果、存在しなかったので「false」となりました。「次へ」を押して2つ目をみると、「京都」も存在しないので「false」。
3つめの「大阪」は含まれるので「true」が表示されました。前に確認したベン図でファイルAのなかの「大阪」は対象ではないことがわかっているので、「true」は不要。「false」のものを集めれば良いことがわかります。
判定ができたら今度は条件分け
さきほど作った式によって、含まれるか含まれないかの判定ができましたので、条件分けをして、条件が「false:になった値を取り出してやります。
「条件」アクションをつかうと、こんな感じになります。
先程の「作成」の名前をわかりやすいように「判定」に変えました。
条件の左辺に「判定」の出力をセットし、右辺には式に「false」と書いてOKを押したものをセットします。
つまり、BC配列に含まれない場合には「はい」のほうに流れるような動作となります。
「はい」の側には「配列変数に追加」アクションを加えて、さきほど初期化した「結果」という変数に「item()」を式として与えます。
配列変数に追加した結果を確認するために、Apply to eachの外側に新たに「作成」をつくって、その中に「結果」変数をセットしておきます。
最後にファイル化する
配列として必要な値をチョイスできたので、最後にこの結果をファイルにしてふたたびSharePointに置きたいと思います。
一番かんたんなのは、結果配列をCSVにすることでしょう。ただし、配列そのままではCSVテーブルにできないので、いったん「選択」アクションを使ってJSONの形にし、それを「CSVテーブルの作成」アクションに渡します。
さらに、その結果をSharePointの「ファイルの作成」アクションのファイルコンテンツに渡すことで、ファイル化することができます。
「作成」はチェック用なので消しても構いません。
ファイルが化けてる!
テスト実行すると、指定したSharePointの場所にCSVファイルができています。よしよし。
安心してください。この文字化け対策ができます。以前に記事にしていますので詳しくはこちらをどうぞ。
原因は記事にゆずるとして、改善させるには、以下の式をファイルコンテンツに設定した「出力」の手前に加えてやるだけです。
decodeUriComponent('%EF%BB%BF')
ふたたび実行して、結果のCSVファイルを開くと、文字化けも解消していました!
まとめ
今回のポイントは以下のような操作です。
- 配列化には「選択」アクションが使える
- ふたつの配列を合体してユニークな配列にするにはunion関数を使う
- 配列に値がふくまれるかどうかはcontains関数をつかうと、結果がtrueかfalseかで返ってくる
- 結果の確認には「作成」が便利
- apply to each で繰り返して取り出す中身は「item()」で取り出す
- 値をキープするには配列の変数を初期化しておく必要あり(作成だとループするたび上書きされるので保持できません)
- 配列から「CSVテーブルの作成」アクションを使うにはいちどJSON化する必要あり
- CSV文字化けはおまじない式をコンテンツの手前に加える
ちょっと待て! それでいいのか?
実はいつもお世話になってるおいしみさんより、ご指摘いただきました。
ループのところってアレイのフィルター処理ではできないんでしょうか?
— おいしみ (@ksgiksg) September 20, 2025
おっしゃるとおり! Apply to eachをつかわずとも、もっと高速処理できる方法が有るじゃあーりませんか!
なぜ Apply to eachが良くないのか
それを確かめるために、ファイルAに47都道府県を突っ込んで実行してみます。
データはCopilotで作ります。
ファイルAのテーブルに47都道府県を貼り付けて実行してみました。
Apply to eachの右肩のところに実行に必要だった時間が15秒と表示されています。この程度の数ならばさほど気になりませんが、もっとデータが多くなったときに、処理に時間がかかるのは想像できます。
リファクタリングしよう!
さきほどの、Apply to eachの手前に「アレイのフィルター処理」アクションを加えて比較します。
「差出人」の欄には、Apply to eachで最初の欄にセットしているのと同じ「選択A」(配列化したファイルA)をセットします。左辺の中身の式は、Apply to eachの中で「判定」の中につかっていたものと同じものです。
contains(outputs('BC配列'),item())
こうして並べてみると構造はほとんど同じ。実際に動かしてみます。
なんと結果はこのとおり。Apply to eachを使ったループ処理は15秒もかかるのに対して、「アレイのフィルター処理」は0秒で結果を出してしまいました。
こんなに向上するならApply to eachと差し替えるしかないですね。
Apply to eachの部分と、内容を確認するためにおいてあった「作成」を削除し、配列をJSON化するための「選択」アクションの「開始」欄には「アレイのフィルター処理」の「本文」をセットします。
ループ処理のなかでつかっていた変数も使わなくなったので、先頭に用意していた変数の初期化も削除して大丈夫です。
後記
今回の記事は2025年9月に大阪で参加した Japan Power Apps User Group #大阪 の勉強会の帰りに、参加者の方にいただいた課題を実践してみたものです。
リクエストには答えられていたでしょうか?
containやunion関数をつかうとPower Appsでも論理演算が行えます。
今回は3つの要素から取り出しましたが、この他にも集合の解決方法についてはふらりさんがとても丁寧な記事をブログでまとめていらっしゃるので参考になります。
こんな人が書いてます
こちらの記事はランゲルハンス島のDDが紹介しました。ブログでクラウドフローのTIPSのようなものを書いたり、Qiita記事を書いたりしていますのでご贔屓に。
フォローやいいねいただけると嬉しいです。
関西のPowerPlatform系の勉強会にときどき出没しますので、気軽に声をかけていただけると喜びます!