【Power Query】List.TransformManyを使ってリストの共通部分を取得

Power Query

こんにちは!ウメハラ(plumfield56)です。

今回は公式サイト上でも情報が少ないList.TransformManyの使い方について解説していきます。
Power Queryだとfor文とか繰り返し分が少なくて苦戦する方が多いと思いますが、List.TransformManyを使用するとfor文に近いことが行えます。

List.TransformManyの使い方

まず公式サイトを見てみましょう。

List.TransformMany - PowerQuery M
詳細については、以下をご覧ください。List.TransformMany
List.TransformMany(list as list, collectionTransform as function, resultTransform as function) as list

引数としては3つとります。

List.TransformMany(list as list, collectionTransform as function, resultTransform as function) as list

1つ目の引数 list

変更したいリストを入力します。
ここで指定したリストは2つ目の引数内の関数で(x as Any) => と指定することでリスト内よう要素を1つづつ取り出して処理することが可能です。
Anyはデータ型は何でも問題ないということです。

2つ目の引数 collectionTransform

この箇所では上記でも記載した通りリストの用を1つずつ取り出して処理をする関数を記載します。
記載方法は大きく2通りあります。

List.TransformMany(list, (x) => 処理, ...)
List.TransformMany(list, each 処理, ...)

どちらでも同じ動きになるので好みの方を使えますが、eachで書いているケースの方が多いように思います。
個人的には(x) => で書いた方がわかりやすいです。
さらに処理の中で2つ目のリストを記載することによって3つ目の引数で(x as Any, y as Any) => と処理をすることが可能です。

3つ目の引数resultTransform

上記で記載した通り、1つ目のリストと2つ目のリストの要素を (x, y) で受け取って処理を行います。戻り値は処理された内容がリストとして戻ります。
for文のネストをイメージして頂くとわかりやすいと思います。

実際にコードを書いて確かめましょう

実際にコードを書いて動作を確認していきます。

let
    list1 = {"a", "b", "c", "e"},
    list2 = {"b", "e"},
    list3 = List.TransformMany(list1, (x) => list2, (x, y) => {x, y})
in
    list3
List.TransformMany(list1, (x) => list2, (x, y) => {x, y})

この箇所は左記にも説明した通り(x) =>でも実行できるので下記に変えて頂いても大丈夫です。

List.TransformMany(list1, each list2, (x, y) => {x, y})

エディター画面にもどると下記のようにListっが8つ作られています。

カーソルを当てて、1つずつ見てもよいですが、見やすいようにテーブルで出してあげると下記になります。

let
    list1 = {"a", "b", "c", "e"},
    list2 = {"b", "e"},
    list3 = 
    Table.FromRows(
        List.TransformMany(list1, (x) => list2, (x, y) => {x, y})
    )
in
    list3

どのような動きをしているか

なぜこのような出力結果になっているのか、どのような処理が行われているのかを図にして表してみました。
わかりやすいようにリストは変数ではなくて入れた状態にしています。

このようにx,yに順番にリストの値が代入されるので、{x, y} で作られたリストを出力すると上記の結果になっています。

活用方法

例えば2つのリストに共通で入っている内容を取得することができます。

let
    list1 = {"a", "b", "c", "e"},
    list2 = {"b", "e"},
    list3 = List.TransformMany(list1, (x) => if List.Contains(list2, x) then {x} else {null}, (x, y) => y)
in
    list3

まず実行結果を見てみると下記のようになります。

なぜこうなるかというと、2つ目の関数部分でlist1の各要素を取り出したときにlist2と一致するかを確認して、
一致する場合 ⇒ {x} を y に引き渡す
一致しない場合 ⇒ {null} をyに引き渡す
という処理を行っています。
3つ目に渡せるのはリストのみなので注意ください。
そのまま渡されたyがリスト形式で戻るので実行結果になります。
この結果にnullは不要なのでList.RemoveNullsを使ってリストからnullを消す処理を加えれば、一致する要素のみのリストが作成できます。

let
    list1 = {"a", "b", "c", "e"},
    list2 = {"b", "e"},
    list3 = List.RemoveNulls(List.TransformMany(list1, (x) => if List.Contains(list2, x) then {x} else {null}, (x, y) => y))
in
    list3

each派の方はこちらを参考にしてください。
eachの場合は _ を使って受けとります。

let
    list1 = {"a", "b", "c", "e"},
    list2 = {"b", "e"},
    list3 = List.RemoveNulls(List.TransformMany(list1, each if List.Contains(list2, _) then {_} else {null}, (x, y) => y))
in
    list3

この処理を応用するするとリストの中から特定の文字列から始まっているもののみを取り出すことも可能です。
Power Queryではfor文が使えないので理想の処理が行いずらいですが、List.TransformManyを使えば処理の幅を増やすことができます。

コメント

タイトルとURLをコピーしました