複数の語句をまとめて置換する


既定の関数では、1つの語句を置換するText.Replace()しか見当たらないようなので、まとめて複数語句を消したり、置換するコードを書いてみました。
2019/9/14追記:パターン2にもっと短いバージョンを書き加えました。

サンプルデータ

詳細エディタに貼ってみてください。

サンプルテキスト
let
    Source = "H4sIAAAAAAAEAK2Wz09jVRTH9/xVFjOJrkxYaVyYTGbhwsRMDLp+p0anQEtLB5mNsfPSIC0Q6DQF+VEIpHR40hJHCYbYcXDMQAFJSnRhRhd+znnvta/VGBemeX333XvuOd/zPT/ulR+lIBUnJuE7JxdSl5JcicfXnKyIK2W5YW5ODlnVcZF3W1pSkaozzNsVT7b7pJupETlm/XAsh952oCvc30z/Isfp3zK/yhpfVVkUL/mBzIcah5xXFEb2Zd+mohNjW0M2mNljZU12eOp9ajooqAGhJatSV/nJnzF/7sQyL1C/n01K8d6l7DqxIWc0/m48Gx+NJ+Ij8dvxEScWf1PfNh4e+rdF50MFWJ9ZkH2YuJTazL0o9lEwP9cHjCWWm/JUHkGfWt9ipg5lPaKO8WgOieeybCQ9ZgcPo7xsy3HqcznFz9p4DD9mA9JfSD5EEeGULRuSn9hCsWvIcDv5AEoKjJ9JDfOd1BlArlBdR40hVloTWajDSKqNZB3z6g2UGtCufrSXlHj1MIf4NsifYuorM11g7pyvTShAbnA5x68VLoeyveUzWTIaS+KqQCjtC3gmsBMRUPoTjZ6bAO/gSrUXBuJPGuNyk5nq/xLvKK8RXhqZN+SYXBjIgz7mPHDrUwn3AyfjQHqkvLI36ffkO3xoy950UflIvybfD8a7TnxhwolpNOUbYumS4ss6o4UwPWc4d2yThzKluTH1iXjZPxUhyGctNr4P4ahgyVVjxpKOZGtHanHm7ek/ULavhsnTHa2sgVKnxrTYpXP/o+lEkK9+wPrKImCEvHxoAlU58x0bDeqlq9XGBcY32C1r0DPfmn03SFs2Mp+jso1HSCj2ZC1UpyRgB4PL1g20CwSmNqwotYMVUURMneFEldI8sApqdDcrk3nezZD8Ep/9SdDlQK5hYAV7XzNDkYDHw0Qx0iZRPXY02AgnDqZetwApg2U8nJeGhnbyjhYzOoLQwGTNL70TM/0I8cBdYNmWaIq4Ud5P+OW7zcj+gfOYkRotmZ5ei+rcjwehm1cFPmXq9awTiy5BzF5AY9Askg+zL+nQe5O3BqRWe2FGCdY8a+V9gkaQ4nOpdX2eUfmaVG6U/Qjj5mUQCWCf8uvMbNIJPGyuWapXUUKvx34xGO37rbB3QhD2CjFbUkjWLX7QWvTjkb2RXfHGPvYLx0/SBflJk6476h4Gt4xxTZij9Drx06IM86RAdri+M+kj/yybKI+/ipOLkP5k8v2wGQ8eH62ehZ7np30ZpF9PoHGROFSsMWgf6bYAxTLzJUzUohZa1l/Vj2uWtJy29RzgMHJlHcb1GMrrfYAz4e74F3poovTEohHz32EnYlS1atnyD1piSYNIvwO4Fu6Tm6m7lEWZaNLSEuuW5FW9L7CqAXT/i4d5YlBhg7U+zYTgmItcGOx0Yss1mGoRZUWZy06B4Bp8qzDb372L3Ht2wdxh/xWFduFLcF0os2PBv1CQrX7YOtO/y3zYbzXUpnnXArzWf/RgnQuHtP3EyWt9RMszSI1AbxO9rnF5ET1Z7FhrygHsLv1jgtC2rElZNlnjguwtPeD/Tmp94K7gkwihY+fSmSgkP6UH5MyPAJse8/Hb/ultb1SallOQXPntafJwMAgVu2gEHS29wuKlVA1V2E73zLimhZdNZrYevOWvfLZpVPYc/QvHXnlEmgoAAA==",
    Custom1 = Binary.Decompress( Binary.FromText(Source),Compression.GZip),
    Custom2 = Text.FromBinary(Custom1,932)
in
    Custom2

パターン1:指定した語句を全部抹消したい。

消去対象のテキストのリスト

消去リスト
let
    Source = "WyJcdTY4NDNcdTU5MmFcdTkwY2UiLCJcdTMwNGFcdTMwNThcdTMwNDRcdTMwNTVcdTMwOTMiLCJcdTMwNGFcdTMwNzBcdTMwNDJcdTMwNTVcdTMwOTMiLCJcdTY4NDMiXQ==",
    Custom1 = Binary.FromText(Source),
    #"Imported JSON" = Json.Document(Custom1,932)
in
    #"Imported JSON"

コード

= List.Accumulate(消去リスト,
                  サンプルテキスト,
          (state,current)=>Text.Replace(state,current,"")
 )

完成図

狙い通り主語が登場人物が消えています。

パターン2:指定した表のとおり、置換したい。

あらかじめ変換表の形で用意しておいてから、それにしたがって、変換します。

語句の変換表

t_指示テーブル
let
    Source = "H4sIAAAAAAAEAI2QOw6AIBAF70JtgSxfryIWuMINqIx3dxcrDRqbyf7ekDDvIlazlhyrRzuKiVrrNdAwqBRrkJjFQENXLN1Y2FZaFY/tEuSmmQWZON5Sx9Bxg9SJaXyrW9oYZoD3d5yC9O1zstXqn++Z6rv5H74cWV0Ux3IClZSxMUcBAAA=",
    Custom1 = Binary.Decompress(Binary.FromText(Source),Compression.GZip),
    #"Imported JSON" = Table.FromRecords(Json.Document(Custom1,932))
in
    #"Imported JSON"

コード(旧)

あとで取り出しやすいように、一度関数(fx_複数置換)を仕上げてから、実行させています。

q_複数置換
[fx_複数置換=
 (対象 as text,指示表 as table,C1 as text,C2 as text)=>

    List.Last( List.Generate(()=>[id=0,vstr=対象],
                             each [id]<=Table.RowCount(指示表),
                             each [id=[id]+1,
                                   vstr=Text.Replace([vstr],
                                                  Record.Field(指示表{[id]},C1),
                                                  Record.Field(指示表{[id]},C2)
                                         )
                                  ],
                             each [vstr]
                )
    ),
 実行=fx_複数置換(サンプルテキスト,t_指示テーブル,"対象","置換後")
][実行]

コード(新)

以前、コンペで考えたときに、もっと簡単な方法を思いついていましたが、載せるのを忘れてました。
たった、これだけです。
※空のクエリを作って、数式バーに貼ってください。

q_複数置換Ver2
= List.Accumulate(
  Table.ToRows(t_指示テーブル), //リストの各項目を用いた繰り返し処理。
    サンプルテキスト,        //初項。最初のxはこれになる。
   (x,y)=>Text.Replace(x,y{0},y{1}) //{0},{1}はリストの項目の取り出し方の記法。
 )

Ver2の解説

みんな大好きList.Accumulate関数を使いました。
第一引数にあるリストの最初から、最後までの全項目を使って、何かやるときに使います。
公式の雪嶺ではstate,currentで関数を作ってますけど、要は順番と名前を間違えなければ、何でもいいんです。
※僕はxとyとか、aとbにすることが多いです。日本語で、「先」と「後」でもいいわけです。

図解
Table.ToRows関数の結果の様子

List.Accumulate関数の進行の様子

x y 処理後の結果
1周目 サンプルテキストの中身 桃太郎 (桃太郎⇒ピーチ太郎置換後の文字列)
2周目 1周目の処理後の結果 おじいさん (さらに、おじいさん⇒お爺置換後の文字列)
3周目 2周目の処理後の結果 おばあさん (さらに、おばあさん⇒おばあ 置換後の文字列)
4周目 3周目の処理後の結果 (さらに、桃⇒モモ置換後の文字列)

公式の記事はこちら。(いずれも英語)
List.Accumulate - PowerQuery M | Microsoft Docs

Expressions, values, and let expression内のリストの箇所

List.Accumulate関数を本格的に使っている @PowerBIxyz さんの記事はこちら。
僕もこの記事を書いていただいてから、使い始めました。
Power Query の List.Accumulate 関数ってなんだよー - Qiita

完成図

変換されてますね。

おまけ:パターン1別法

Splitter関数群にちょうどいいものがあるので、こちらの方がスッキリします。
ただ、僕は忘れそうだなと思ったので、Splitterを別法としました。

Text.Combine(
 Splitter.SplitTextByEachDelimiter(消去リスト)(サンプルテキスト)
)

参考:レファレンス
Splitter.SplitTextByEachDelimiter | Microsoft Docs