PowerApps│感謝を送り合う社内SNSアプリの作り方 ⑦ランキングと入れ子

こんにちは、あんこ先生です。

前回のマイページで、自分が送った数と受け取った数を出しました。

今回はそれを部署のみんなに広げます

そう、ランキングです。

誰がいちばんありがとうを送ったか、誰がいちばん受け取ったか。

上位3人にはメダルも付けて、ちょっと盛り上がる画面にしましょう。

この記事で作るもの
・部署メンバー全員の集計ランキング
・「送った数」と「受け取った数」の切り替え
・「累計」と「今月」の切り替え
・上位3人のメダル表示

まだ読んでいない人は、先にマイページの回まで進めておいてくださいね。

やることは、前回の応用

考え方は前回とまったく同じです。

前回は「自分」の投稿を数えました。

今回はそれをメンバー全員ぶん、順番に数えるだけ。

そして数の多い順に並べれば、ランキングのできあがりです。

使うのは、第1回で作った glbMembers ですね。

部署メンバーの一覧がすでに変数に入っているので、これを1人ずつ見ていきます。

画面を用意する

新しい画面を追加して、名前を scrRanking にします。

上から、切り替えボタンが2段、その下にランキングの一覧という並びです。

切り替えの状態は、コンテキスト変数で持ちます。

変数入る値意味
ctxRankMode“Sent” / “Received”送った数 / 受け取った数
ctxRankPeriod“All” / “Month”累計 / 今月

OnVisible では、この2つを初期化してから集計します。

//画面 scrRanking の OnVisible(初期表示は「送った数・累計」)
//メンバー1人ずつに Cnt(件数)の列を足して、多い順に並べる
UpdateContext({ ctxRankMode: "Sent", ctxRankPeriod: "All" });

ClearCollect(
    colRanking,
    SortByColumns(
        AddColumns(
            glbMembers As member,
            Cnt,
            CountRows(Filter(Posts, Author.Email = member.Email))
        ),
        "Cnt",
        SortOrder.Descending
    )
)

AddColumns で、メンバーの一覧に Cnt という列を足しています。

中身は「そのメンバーが送った投稿の数」です。

あとは SortByColumns で多い順に並べれば、もうランキングになっています。

ここで丸一日ハマりました

さて、上の式で As member という見慣れない書き方をしているのに気づいたでしょうか。

じつはこれが、この記事でいちばんお伝えしたいところです。

わたしは最初、素直にこう書きました。

//一見よさそうだけど、一覧が空っぽになる書き方
AddColumns(
    glbMembers,
    Cnt,
    CountRows(Filter(Posts, Author.Email = ThisRecord.Email))
)

「いま見ているメンバーのメール」を指したくて ThisRecord と書いたわけです。

ところが実行してみると、ランキングがまるごと空っぽ

エラーも出ない。赤い波線も出ない。ただ何も表示されない。

「この行」が、どの行か分からない

原因は、ThisRecord の指す先でした。

この式、じつは2つの繰り返しが入れ子になっています

外側は AddColumns で、メンバーを1人ずつ見ている。

内側は Filter で、投稿を1件ずつ見ている。

この状態で「この行」と言うと、いちばん近い内側、つまり投稿のほうを指してしまうんですね。

メンバーのメールアドレスを見たかったのに、投稿のメールアドレスを見にいっていた、というわけです。

名前をつけて呼ぶ

解決策はシンプルで、外側に名札をつけます。

//外側のメンバーに member という名前をつけて、内側から名指しで呼ぶ
AddColumns(
    glbMembers As member,
    Cnt,
    CountRows(Filter(Posts, Author.Email = member.Email))
)

As member と書くだけで、外側の行に「member」という名前が付きます。

あとは内側から member.Email と名指しで呼べば、迷いようがありません。

ここが個人的に一番おもしろい:エラーが出ないのが厄介
この落とし穴のいやらしいところは、Power Apps が怒ってくれないことです。どちらの書き方も文法としては正しいので、赤い波線も警告も出ません。ただ結果だけが違う。しかも「空っぽ」という、原因の手がかりが少ない形で。わたしはコレクションの中身を確認するところから始めて、ようやく気づきました。繰り返しの中に繰り返しを書いたら、As で名前をつける。これを習慣にしておくと、こういう静かなバグを避けられますよ。

切り替えボタンを作る

次は「送った数 / 受け取った数」と「累計 / 今月」の切り替えです。

モダンボタンを4つ並べます。

ボタン名表示Appearance
btnModeSent送った数If(ctxRankMode = “Sent”, ButtonAppearance.Primary, ButtonAppearance.Transparent)
btnModeReceived受け取った数If(ctxRankMode = “Received”, ButtonAppearance.Primary, ButtonAppearance.Transparent)
btnPeriodAll累計If(ctxRankPeriod = “All”, ButtonAppearance.Secondary, ButtonAppearance.Transparent)
btnPeriodMonth今月If(ctxRankPeriod = “Month”, ButtonAppearance.Secondary, ButtonAppearance.Transparent)

選ばれているほうだけ色が付くので、いまどちらを見ているのか分かります。

押したときは、状態を変えてから集計をやり直します。

//「送った数」ボタン btnModeSent の OnSelect
//モードを Sent に変えてから、期間の状態にあわせて集計しなおす
UpdateContext({ ctxRankMode: "Sent" });

ClearCollect(
    colRanking,
    SortByColumns(
        AddColumns(
            glbMembers As member,
            Cnt,
            CountRows(
                Filter(
                    Posts,
                    Author.Email = member.Email,
                    If(ctxRankPeriod = "Month", Created >= Date(Year(Now()), Month(Now()), 1), true)
                )
            )
        ),
        "Cnt",
        SortOrder.Descending
    )
)

「今月」のときは Created が今月1日以降のものだけを数えています。

Date(Year(Now()), Month(Now()), 1) で「今月の1日」を作っているんですね。

ほかの3つのボタンも、同じ形で書きます。

押した時点で決まる軸は固定値、もう一方の軸だけ変数で分岐させると、条件が浅くなって読みやすいですよ。

ここが落とし穴:まとめようとして遠回りした話
同じような式を4回も書くのは気持ち悪いですよね。わたしも最初、App.Formulas に名前付き数式としてまとめようとしました。

RankingTable(mode: Text, period: Text): Table = ...

ところが呼び出すと「不明またはサポートされていない関数です」と怒られてしまいました。引数を取るユーザー定義関数はまだプレビュー機能で、環境の設定によっては使えないんですね。配布するアプリや、環境の差を気にしたくない場面では、素直に各ボタンに書くほうが確実です。そのぶん長くなりますが、条件の書き方を工夫すれば、そこまで読みにくくはなりませんよ。

ランキングを並べる

いよいよ一覧です。

ギャラリーを置いて名前を galRanking にして、ItemscolRanking を指定します。

コントロールプロパティ設定する式
lblRankText順位またはメダル(下で解説)
avaRankImageThisItem.Picture
lblRankNameTextThisItem.DisplayName
lblRankCountTextText(ThisItem.Cnt) & ” 件”
lblRankCountAlignAlign.Right

アバターの ThisItem.Picture に注目してください。

第1回で glbMembers に写真を入れておいたので、ここでもコネクタを呼ばずに済んでいます。

上位3人にメダルを出す

順位のところは、少し工夫します。

//ラベル lblRank の Text(1〜3位はメダル、4位以降は数字)
//順位=自分より件数が多い人の数 + 1
With(
    { rank: CountRows(Filter(colRanking, Cnt > ThisItem.Cnt)) + 1 },
    Switch(
        rank,
        1, "🥇",
        2, "🥈",
        3, "🥉",
        Text(rank)
    )
)

順位の出し方が、ちょっとおもしろいところです。

自分より件数が多い人が何人いるかを数えて、そこに1を足す。

自分より上が0人なら1位、2人いれば3位、という具合ですね。

この書き方なら、同じ件数の人は同じ順位になります。

3件の人が2人いたら、どちらも1位。次の人は3位です。スポーツの順位と同じ考え方ですね。

ひとことメモ:Align.End は無い
件数を右寄せにするとき、Align.End と書きたくなるかもしれません。Webの世界だと start / end という言い方をしますからね。でも Power Apps にあるのは Align.LeftAlign.CenterAlign.Right の3つです。右寄せは Align.Right と覚えておいてください。

知っておきたいこと

委任の警告について

メンバー全員ぶん、投稿を絞り込んで数えています。

そのため、委任の警告(青い下線)が出ることがあります。

部署内で25人ほど、投稿が数百件のうちは問題ありません。

もし数千件を超えるようになったら、集計を Power Automate に任せて結果だけ持ってくる、といった作りに変える時期ですね。

委任については、黄色いアイツ、委任警告の回避方法にくわしくまとめてあります。

集計のタイミング

ランキングは OnVisible と切り替えボタンのときだけ計算しています。

開きっぱなしにしていると、その間に増えた投稿は反映されません。

気になるようなら、更新ボタンを置いて同じ式を実行させるといいですよ。

まとめ

今回はここまでにしましょう。

・ランキングは、マイページの「数える」をメンバー全員ぶんに広げただけ。
AddColumns の中に Filter を書くときは、外側に As で名前をつける。ThisRecord は内側を指してしまう。
・エラーが出ないのに結果だけおかしいときは、入れ子の中を疑う。
・順位は「自分より多い人の数+1」で出すと、同着もきれいに揃う。
・引数付きの名前付き数式はプレビュー機能。環境差が心配なら各所に直接書く。

これで、投稿・タイムライン・いいね・コメント・マイページ・ランキングと、当初決めた機能がすべて揃いました。

7回にわたっておつきあいいただき、ありがとうございました。

部署のみなさんが「ありがとう」を送り合う様子を想像しながら作ると、けっこう楽しいものですよ。

タグの文言を自分の職場に合わせて変えたり、ランキングの期間を四半期にしてみたり。

ぜひ、あなたの職場に合う形に育ててみてくださいね。

それでは、また次の記事でお会いしましょう。

ABOUT US
七草あんこ
非IT系中間管理職やってます。社命によりoffice365を主軸とした業務改善プロジェクトメンバーに任命されたことでPowerAppsと出会えました。いまではビジネス・プライベートを問わず、欠かせないツールになっています。導入初期やアプリ作成時に遭遇した諸問題の解決法とサンプルアプリの作り方を紹介していきます。主にTwitterで情報収集しているので不明点など呟いているとお邪魔するかもしれません。