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

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

##サンプルデータ
詳細エディタに貼ってみてください。

```PQ:サンプルテキスト
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
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370992/c74e795c-4d77-26e8-780a-6810388d3fa7.png)


##パターン１：指定した語句を全部抹消したい。
###消去対象のテキストのリスト

```PQ:消去リスト
let
    Source = "WyJcdTY4NDNcdTU5MmFcdTkwY2UiLCJcdTMwNGFcdTMwNThcdTMwNDRcdTMwNTVcdTMwOTMiLCJcdTMwNGFcdTMwNzBcdTMwNDJcdTMwNTVcdTMwOTMiLCJcdTY4NDMiXQ==",
    Custom1 = Binary.FromText(Source),
    #"Imported JSON" = Json.Document(Custom1,932)
in
    #"Imported JSON"
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370992/44e76d78-f350-f64a-b245-d890111dc9a4.png)


###コード

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

###完成図
狙い通り主語が登場人物が消えています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370992/b8a062ed-d107-56e8-c3e3-b550d33a7dc4.png)



##パターン２：指定した表のとおり、置換したい。
あらかじめ変換表の形で用意しておいてから、それにしたがって、変換します。

###語句の変換表

```PQ: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"
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370992/ade58d11-1a3e-9041-9bc4-f0796dab329c.png)


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

```PQ: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_指示テーブル,"対象","置換後")
][実行]
```
###コード(新)
以前、[コンペ](https://xxlbi.com/blog/text-replacement-competition/)で考えたときに、もっと簡単な方法を思いついていましたが、載せるのを忘れてました。
たった、これだけです。
※空のクエリを作って、数式バーに貼ってください。

```PQ: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関数の結果の様子
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370992/701f343b-d208-6417-5aa9-e583bb62fec1.png)

List.Accumulate関数の進行の様子

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



公式の記事はこちら。（いずれも英語）
[List.Accumulate - PowerQuery M | Microsoft Docs
](https://docs.microsoft.com/en-us/powerquery-m/list-accumulate)
[Expressions, values, and let expression](https://docs.microsoft.com/en-us/powerquery-m/expressions-values-and-let-expression#list)内のリストの箇所

List.Accumulate関数を本格的に使っている @PowerBIxyz さんの記事はこちら。
僕もこの記事を書いていただいてから、使い始めました。
[Power Query の List.Accumulate 関数ってなんだよー - Qiita
](https://qiita.com/PowerBIxyz/items/292c3cfa8a163e7a2ace)


###完成図
変換されてますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370992/8e4cce68-794a-113f-2da7-3e9fd337e09d.png)

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

```
Text.Combine(
　Splitter.SplitTextByEachDelimiter(消去リスト)(サンプルテキスト)
)
```
参考：レファレンス
[Splitter.SplitTextByEachDelimiter | Microsoft Docs](https://docs.microsoft.com/ja-jp/powerquery-m/splitter-splittextbyeachdelimiter)