Power Apps のコレクションを使いこなそう


# はじめに

Power Apps の変数 (一時的なデータ格納先) の一つとしてコレクションがございます。

個人的に多くのアプリで利用しており、コレクションを上手く使いこなせるとアプリケーションの幅が広がると思っています。しかし、最初のうちはピンとこない、どんなときに利用するかイメージが湧かないという人もいると思います。

そのため、今回は、コレクションについて、特に、私自身がどんなときに使っているかと言う観点で整理してみたいと思います。

# コレクションとは

上述の通り、Power Apps の変数 (一時的なデータ格納先) の一つで、以下のようなテーブル構造のデータを格納します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/0499f44d-ed28-dc90-78db-00c6bebff57c.png)

例えば、以下は、これまでブログでよく紹介している Azure OpenAI の ChatGPT と連携するアプリですが、こちらのチャット部分のデータは実はコレクションになっており、そちらをギャラリーコントロールで表示しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/08c68ef7-f186-a3a6-3402-7e403f6025be.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/7d8dac00-a5b0-9d19-162b-53a1aff322e1.png)

# どんなときに使うのか

具体的なアプリを例に、私自身、どんなときにコレクションを使っているのか改めて整理してみます。

## 同じデータを何度もネットワーク経由で取得しない


まず、よくアプリに利用者のプロフィール情報を記載するような場合があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/0ec0420d-1dbb-9c57-38d6-94d3236abc96.png)

アプリに複数のページがあると、ページ遷移するたびにネットワーク通信してプロフィール情報を取得することに効率が悪いです。

そのため、アプリ起動時にプロフィール情報をグローバル変数に格納しています。

```
 Set(
        gblCurrentUser,
        Office365ユーザー.MyProfileV2()
    )
```

こちらはグローバル変数でいいのですが、対象データがテーブル形式の場合はコレクションに格納する感じになります。

例えば、アプリ内でデータソース (テーブル形式)　のデータを何度も取得する場合、毎回ネットワーク越しに通信が発生してしまうことになります。

もちろん、最新の情報が必要な場合は都度データソースに通信した方がいいですが、そんなん高頻度でデータを取得する必要がない場合は、例えば、アプリ起動時にデータソースに接続して一旦コレクションにデータを格納し、それ以降はコレクションのデータを閲覧する方が効率が良いです。

例えば、以前以下の記事で紹介した働き方を共有するアプリでは、月を移動すると翌月のデータを参照して編集や削除ができますが、画面表示時にコレクションにデータを格納し、月を移動した際は、その月の情報をベースにコレクションのデータをフィルターして表示しています。

こちらを実施しない場合は、月を変更するたびにデータソースに通信してデータを取得することになり、処理効率が悪いです。

https://qiita.com/Takashi_Masumori/items/7db650461849b8dab820

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/17a4381a-8dd2-13a7-7aa8-0ef3723a0a4d.png)

一応、データの変更をした際はそのタイミングでコレクションをリフレッシュするようにしており、ネットワーク通信する回数を必要う最低限にしています。

## コネクタの戻りのデータを格納する

以下は、これまでブログ記事でも紹介してきた日程調整を効率化するアプリですが、以下の候補日時のデータについて、コレクションを利用しています。

https://qiita.com/Takashi_Masumori/items/bc3c4588a18ad852a246

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/6d1a5fe3-5839-e978-ff97-72cebf78b863.png)

コレクションを作成している処理は以下のような感じです。

```
ClearCollect(
    colMeetingTimes,
    Sort(
        AddColumns(
            // https://docs.microsoft.com/ja-jp/connectors/office365connector/#find-meeting-times-(v2)
            Office365Outlook.FindMeetingTimesV2(
                {
                    RequiredAttendees: Concat(
                        colAttendees,
                        Mail & ";"
                    ),
                    MeetingDuration: drpDuration.Selected.Minutes,
                    Start: dteStartDate.SelectedDate,
                    End: DateAdd(
                        dteEndDate.SelectedDate,
                        24,
                        TimeUnit.Hours
                    ),
                    MaxCandidates: 30,
                    IsOrganizerOptional: false,
                    ActivityDomain: If(
                            chkIsContainPersonalTime.Value,
                            "Unrestricted",
                            "Work"
                        ),
                    MinimumAttendeePercentage:  If(
                        chkShowCandidateAllAttendeesUnableToAttend.Value,
                        50,
                        100 )
                }
            ).meetingTimeSuggestions,
            "StartTime",
            DateAdd(
                DateTimeValue(meetingTimeSlot.start.dateTime),
                -TimeZoneOffset(),
                TimeUnit.Minutes
            ),
            "EndTime",
            DateAdd(
                DateTimeValue(meetingTimeSlot.end.dateTime),
                -TimeZoneOffset(),
                TimeUnit.Minutes
            )
        ),
        StartTime
    )
);
```

少し長いですが、ポイントは、 `Office365Outlook.FindMeetingTimesV2`の部分であり、こちらは、Outlook コネクタの FindMeetingTimesV2 というアクションを意味し、その戻り値は以下のように JSON データになっており、その中の meetingTimeSuggestions という候補日時について、配列データ (複数のデータ) となっております。

こちらの配列について、ClearCollect 関数でコレクションにそのまま格納することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/f1c8df87-42e9-182c-fd36-5ad91b2f3927.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/b14966f8-3575-9f05-2d42-45810bfe612e.png)

このように、コネクタの戻り値の配列データをアプリ内で表示する際にコレクションを利用することがあります。

## データを一括登録する

以前こちらの記事で紹介したようなデータの一括登録をするような機能を実装する際、コレクションを利用しています。

https://qiita.com/Takashi_Masumori/items/8da5e7851da2e6cb0a8b

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/7a32cb00-517a-c799-b9d4-93bfe443e58d.png)

詳細は上記記事を確認いただければと思いますが、追加する複数のデータについて、一旦コレクションに格納し、ForAll という関数を使ってデータを一行ずつ登録しております。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/a6bb74a0-9823-679e-1113-2d8c61bd506f.png)

```
//注文明細
    ForAll(
        colOrderItems,
        Patch(
            注文明細,
            Defaults(注文明細),
            {
                '注文 ID': Value(gblOrderTitle),
                タイトル: ThisRecord.品物,
                単価: Value(ThisRecord.単価),
                数量: Value(ThisRecord.数量)
            }
        )
    );
```

また、以下は、工数記録をするアプリですが、こちらも、複数のデータを一括で登録する機能を実装しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/0384c99f-1913-b35c-3e28-5e7532573809.png)

こちらも一旦データをコレクションに格納し、ForAll 関数でデータを一行ずつ登録しております。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/02b5cee0-cfc0-3ba4-72bc-3f983a58d9b3.png)

```
ForAll(
    colWorkHours As _changeItem,
    If(
        IsBlank(
            LookUp(
                工数記録,
                UPN = gblCurrentUser.userPrincipalName && Project = _changeItem.Project && WorkType = _changeItem.WorkType && Date = _changeItem.Date
            )
        ),
        Patch(
            工数記録,
            Defaults(工数記録),
            {
                Name: gblCurrentUser.displayName,
                Year:Year(_firstDayOfWeek),
                Month:Month(_firstDayOfWeek),
                Week:Text(_firstDayOfWeek, "yyyy年mm月") & Day(_firstDayOfWeek) & "日週",
                Date: _changeItem.Date,
                Department: gblCurrentUser.department,
                Project: _changeItem.Project,
                WorkType: _changeItem.WorkType,
                Hour: Value(_changeItem.WorkHours),
                UPN: gblCurrentUser.userPrincipalName
            }
        ),
        Patch(
            工数記録,
            LookUp(
                工数記録,
                UPN = gblCurrentUser.userPrincipalName && Project = _changeItem.Project && WorkType = _changeItem.WorkType && Date = _changeItem.Date
            ),
            {Hour: Value(_changeItem.WorkHours)}
        );
    )
);
If(
     // エラー判定
    !IsEmpty(Errors(工数記録)),
     // エラーメッセージ表示
    Notify(
        Concat(
            Errors(工数記録),
            Column & ": " & Message
        ),
        NotificationType.Error
    ),
     // 成功メッセージ
    Notify(
        "工数入力処理が成功しました",
        NotificationType.Success
    );
    Clear(colWorkHours);
    Set(
        gblWorkHoursReset,
        true
    );
    Set(
        gblWorkHoursReset,
        false
    );
    Navigate('Work Hours App Top Screen')
);

```

このように、データを一括登録する際、コレクションを利用しています。

## テーブルを加工、結合する

こちらは、入力された工数データを元に、選択した部署、月の人ごとの週あたりの稼働時間を表示しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/1ac7c015-c321-1bac-8248-2488b628999b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/c7f90b8e-24b9-4f01-ffb0-cf0e09f98e56.png)

データベース側の工数記録テーブルには、人ごとの週あたりの稼働時間のデータは持っていないため、GroupBy 関数でグループ化して、更に、合計値を列として追加するような加工を行ったコレクションを作成し、表示しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/180c5c72-b6fc-2943-ff78-9c786ec027f5.png)


```
Set(
    _firstDayOfWeekForView,
    DateAdd(
        dtpMonthForView.SelectedDate,
        1 - Weekday(dtpMonthForView.SelectedDate),
        TimeUnit.Days
    )
);
ClearCollect(
    colWorkHoursView,
    SortByColumns(
        AddColumns(
            GroupBy(
                ShowColumns(
                    Filter(
                        工数記録,
                        Year = Text(Year(_firstDayOfWeekForView)) And Month = Text(Month(_firstDayOfWeekForView)) And Department = drpDepartment.Selected.crcda_department
                    ),
                    "crcda_department",
                    "crcda_hour",
                    "crcda_name",
                    "crcda_week",
                    "crcda_worktype",
                    "crcda_project"
                ),
                "crcda_name",
                "crcda_week",
                "他"
            ),
            "工数",
            Sum(
                他,
                crcda_hour
            )
        ),
        "crcda_name"
    )
);
Navigate('Work Hours View Screen')

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/accb86f4-d874-37da-71cf-52eda8de64b8.png)

このように元のテーブルを加工してアプリ上で表示したい場合にコレクションを利用することがあります。

## その他

以下は、以下の記事で紹介している定例の日程調整をするアプリですが、曜日のデータについて、Power Platform は持っていないため、アプリ起動時にコレクションの作成しています。選ばれた曜日データを元に日付データを生成しており (再利用しており)、その観点からも、コレクションを利用しています。

https://qiita.com/Takashi_Masumori/items/d6f2406f352b4e975d61

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/136672f1-9463-eb1a-841d-80362d3cccdd.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3074511/9d7d14ae-f5ac-64d3-39ba-c820f89a9679.png)

# 基本的な使い方

既に様々な方がブログ記事を書かれているため、コレクションに対する操作については、是非これらの記事を参考になさっていただければと存じます。

https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/create-update-collection

https://hobby-compass.com/powerapps%E3%81%AE%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E8%A7%A3%E8%AA%AC%EF%BD%9C%E4%BD%9C%E6%88%90%E3%81%8B%E3%82%89%E4%BF%AE%E6%AD%A3%E3%80%81/

https://www.cloud-for-all.com/bizapp/blog/power-apps-collection

https://it-dxblog.com/power-apps-collection/

# まとめ

今回は、Power Apps のコレクションを使いこなそうということで、私がアプリを作成するとき、どんなケースでコレクションを利用しているかを実例を交えて紹介しました。

コレクションを上手く使いこなせるとアプリケーションの幅が広がると思うため、少しでも参考になれば幸いです。