Power Query(パワークエリ)はExcelやPower BIでデータを整形・変換するための強力なツールです。
しかし、VBAやPythonのように「for文」や「while文」が存在しないため、「ループ処理ができないのでは?」と感じた方も多いのではないでしょうか。
実際には、Power QueryのM言語でもList.Generate関数や**再帰関数(自分自身を呼び出す関数)**を使うことで、ループ処理と同じような繰り返し処理を実現できます。
この記事では、Power Query M言語でループ的な処理を行うための方法を、初心者にもわかりやすく解説します。
Power Query M言語にはループ文が存在しない
まず理解しておきたいのは、M言語には「for」や「while」といったループ構文が存在しないという点です。
その代わりに、「リスト(List)」を使った繰り返し処理や関数の再帰呼び出しで同等の処理を実現します。
Power Queryは関数型言語であり、「状態を変化させながら繰り返す」のではなく、「データの変換を連鎖させて最終的な結果を導く」という考え方をとります。
そのため、VBAのような命令型言語とは発想を変える必要があります。
List.Generate関数を使ったループ処理の基本
Power Queryでループ処理を実現する代表的な方法が、List.Generate関数を使う方法です。
この関数は、条件が満たされるまでリストを生成する、いわば「while文」のような働きをします。
基本構文
List.Generate(
() => 初期値,
each 条件,
each 次の値,
each 出力内容
)
- 初期値:ループ開始時の値を指定
- 条件:ループを続けるかどうかを判定
- 次の値:ループごとに更新する値を指定
- 出力内容:各ループごとの出力を定義(省略可)
List.Generateの具体例:1~10までのリストを作る
まずはシンプルな例から見てみましょう。
1から10までの整数をリストとして生成する場合は以下のように書けます。
List.Generate(
() => 1, // 初期値
each _ <= 10, // 条件
each _ + 1, // 次の値
each _ // 出力
)
結果:
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
このコードは、VBAでいうところの「For i = 1 To 10」に相当します。
List.Generateは繰り返しのたびに「現在の値(_)」を参照し、それをもとに次の値を計算します。
条件付きループの例:合計が100を超えるまで繰り返す
次に、「合計が100を超えるまで繰り返す」という条件付きのループを作ってみましょう。
List.Generate(
() => [i=1, total=0],
each [total] < 100,
each [i=[i]+1, total=[total]+[i]],
each [i, total]
)
結果(部分):
{
[i=1, total=1],
[i=2, total=3],
[i=3, total=6],
[i=4, total=10],
...
[i=13, total=91],
[i=14, total=105]
}
このように、List.Generateでは複数の変数(レコード)を状態として保持できるため、複雑なループも扱えます。
VBAやPythonの「ループ+カウンター変数+集計変数」に相当する処理が可能です。
List.Accumulateとの違いを理解する
もう一つループに近い処理を行う関数がList.Accumulateです。
これはリストの各要素を順に処理して、ひとつの結果を累積していく関数です。
いわば「for each文 + 合計」や「fold関数」のようなものです。
基本構文
List.Accumulate(リスト, 初期値, (状態, 現在の要素) => 新しい状態)
例:リストの合計を求める
List.Accumulate({1,2,3,4,5}, 0, (state, current) => state + current)
結果:
15
この例では、stateが累積値、currentが現在の要素を表します。
繰り返しごとにstateにcurrentを加算していき、最終的に15を返します。
List.Accumulateは、リストが既に存在する場合に便利で、
List.Generateは、繰り返しながらリストを作りたい場合に使う、という使い分けがポイントです。
再帰関数を使ったループ処理
Power Queryでは、**自分自身を呼び出す関数(再帰関数)**を定義してループを実現することもできます。
例:1からnまでの合計を求める再帰関数
let
SumToN = (n as number) as number =>
if n = 0 then
0
else
n + @SumToN(n - 1)
in
SumToN(10)
結果:
55
ここでは、@SumToNが自分自身を呼び出しています。nが0になるまで繰り返し計算し、結果を合計しています。
再帰関数は、List.Generateでは書きにくい条件分岐やネストが多い処理に向いています。
実用例:日付を1日ずつ増やすループ
実務で役立つ例として、「開始日から終了日までの日付リストを作る」ケースを考えます。
let
StartDate = #date(2025, 1, 1),
EndDate = #date(2025, 1, 10),
Dates = List.Generate(
() => StartDate,
each _ <= EndDate,
each Date.AddDays(_, 1)
)
in
Dates
結果:
{
#date(2025,1,1),
#date(2025,1,2),
#date(2025,1,3),
...
#date(2025,1,10)
}
このように、List.Generateを使えば任意の日付範囲のリストも簡単に作れます。
Power Queryのカレンダーテーブル作成などでもよく使われるテクニックです。
複雑なリスト操作を組み合わせる
List.Generateを他の関数と組み合わせることで、より高度な処理も可能です。
たとえば、APIの複数ページを自動で取得する、テーブルを段階的に展開するなど、
繰り返し処理を動的にコントロールできます。
例:APIからデータをページ単位で取得する疑似コード
List.Generate(
() => [Page=1, Data=GetData(1)],
each [Data] <> null,
each [Page=[Page]+1, Data=GetData([Page])],
each [Data]
)
このような書き方で、データがなくなるまでページを自動的に取得し続けることができます。
再帰とList.Generateの使い分け
| 方法 | 特徴 | 向いている処理 |
|---|---|---|
| List.Generate | 高速でわかりやすい | 単純な繰り返しや増分処理 |
| List.Accumulate | リストを集約する | 合計や文字連結など |
| 再帰関数 | 柔軟で条件分岐に強い | 複雑な条件つきループやネスト構造 |
状況に応じてこれらを使い分けると、M言語でもループ処理が自在に行えます。
まとめ
Power Query M言語には「for」や「while」といった明示的なループ構文はありません。
しかし、List.GenerateやList.Accumulate、そして再帰関数を活用すれば、
ループ処理を柔軟に実現することが可能です。
ポイントをまとめると次の通りです。
- M言語は「リストを操作する」発想でループを表現する
- List.Generateは「リストを作るループ」
- List.Accumulateは「リストを処理して結果をまとめる」
- 再帰関数は「複雑な条件で繰り返す」際に有効
Power Queryで繰り返し処理をスマートに扱えるようになると、
より自動化されたデータ変換フローを構築できるようになります。
