スキップしてメイン コンテンツに移動

Power Query の List がそこそこ便利なことがある


Power BI Desktop や Excel 利用できる Power Query で list がもっとうまく使えたらなぁと最近よく思うんです。繰り返したい処理とかでも使えそうですし。そう思った事の発端は、
とかで MSDN:List.Generate を調べていたからなんだけど、ここまで複雑なことでなくても list を扱えれば便利じゃないかなと考えた次第。よく使う Power Query の関数なかでも引数の型が list なのもそこそこありますしね。
クエリ エディターでの作業の多くは Table に対してのことが多いのだけど、あれこれしているうちに、これって列の追加や値の変換とかでは解決できないんじゃね?とか手詰まりに近いことあったりするんですね。複雑な処理はユーザー定義関数を用意したりして工夫をしたりはするけれども。で、List.Generate を使って何かを解決する前に List と関連する関数にちょっと注目してみた。

List は { } で表現

簡単なところから。
{1, 2, 3}
は、すべてのリストアイテムが数値(number)の List。
{1, "A", 3}

{
    {1, "A"},
    {2, "B"}
}
リストアイテムは型は any で特定されなくてもよいのでネストしたりしても可。型が record や table というのもよく見かけることになるかもしれない。
{1, 2, 3} = {1, 2, 3} // true

{1, 2, 3} <> {1, 3, 2} // true

{10, 20, 20} = {10, 20, 10 + 10 } // true

{"A", "B"} & {"C"} = {"A", "B", "C"} // true
重要なのはアイテムの順番ですね。ほか便利な記法としてはシーケンサ(..)というの使えます。ただし、値や文字コードが昇順になっていないとリストアイテム数が 0 の List になってしまいます。
{1 .. 5} = {1, 2, 3, 4, 5} // true

{"A" .. "C"} = {"A", "B", "C"} // true

List.Count({3 .. 1}) = 0 // true
リストアイテムを参照する { } と間違いのないように。
{"A", "B", "C"}{1} = "B" // true

便利そうなので使ってみた組合せ

Text.ToList:文字列を list に変換
Text.Combine:アイテム(text)の list からひとつの文字列に結合
List.ToList("ABCDE") = {"A", "B", "C", "D", "E"} // true

Text.Combine({"A", "B", "C", "D", "E"}) = "ABCDE" // true
文字列を list に変換 → すべてのアイテム(text)に対し List.Transform などで処理 → 再び結合すれば置換のような処理になる。単純な置換であれば、List.ReplaceMatchingItems でよいかな。
List.ReplaceMatchingItems({"A", "B", "C"}, {{"A", 1}, {"C", 3}}) = {1, "B", 3} // true
リストアイテムにマッチするものを置換してくれるのだけど、引数がリスト{OldText, NewText}のリストだから、
Text.Combine(
    List.ReplaceMatchingItems(
        Text.ToList(
            "平成29年6月15日"
        ),
        List.Zip(
            {{"0".."9"}, {"0".."9"}}
        )
    )
) = "平成29年6月15日" // true
という感じに表現できてよいかも。
もう少し範囲を拡げて 全角英数記号などを半角に変換するならこんな感じでも。
(Source as text) as text =>
let
    Combi = List.Zip(
                 {
                     {Character.FromNumber(0x3000), "!".."~"},
                     {Character.FromNumber(0x20), "!".."~"}
                 }
            ),
    TextList = List.ReplaceMatchingItems(Text.ToList(Source), Combi)
in
    Text.Combine(TextList)