#PowerAutomate で半角全角カナ変換を作る

これまで2回に分けて、半角全角カナ変換Flowのためのテクニックをご紹介してきました。

第一回：[Power Automateで文字列を1文字ずつに分割する方法](https://qiita.com/h-nagao/items/e0153b9a2c700cc2c95e)
第二回：[PowerAutomate で配列に連番を付与する](https://qiita.com/h-nagao/items/77ae960926f839453449)

今回は上の2つを利用して、半角カナから全角カナに変換するFlowの作り方を紹介します。
なお、ここで紹介したFlowは以下にzip形式で配置していますので、試してみてください。
https://github.com/mofumofu-dance/PowerApps365/tree/master/misc

# 方針
まずは戦略から。
「入力された文字列(半角カナ)を1文字ずつに分割して、それぞれを全角カナに変換して最後に結合」という、これだけならすぐにできそうなものですが、問題は・・・**半角カナの濁音が2文字扱いなことです！！**

##半角カナの難しさ
まずは見ていただきましょう。1文字ずつに分割するFlowで紹介した方法で、文字列の長さをとって、Substring関数で分割してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/77a2fa69-98fe-27ba-20d6-b3ce89d420ca.png)
濁点！！濁点1文字ですよ。わかっていたけど、実際こうなると少し悲しいですね。
安直には"単一の文字"に分割して変換できなくなります。

##打開策
Excelでちょっと考えてみました。
下の文字とくっつけて、できた文字列をうまくフィルターすればできそう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/ce0aa9d5-e5c8-f356-1b66-b0f0ad830d7a.png)

「1文字ずらした配列の値」は、もともとSubstringで開始点と文字数を指定しているので、開始点をずらせばよさそう
「単純な分割結果とずらした結果の結合」は、concat関数でくっつければOK
「濁点か半濁点以外をFilter」は、文字通りFilterアクションでできそう。

あとの問題は、**「1文字ずらした結果が濁点か半濁点なら」**の条件分岐ですね。

## IF文はアクションだけじゃなく、数式でも使える
のに気づくまで時間がかかりました。
こんな感じで、Selectの中でifを入れることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/3f311afa-86f5-3004-dc56-90ce779bd199.png)

これ使えば楽勝ですね。ここで挿入した式は以下のものです。

```
if(or(equals(substring(outputs('Compose'),add(item(),1),1),'ﾞ'),equals(substring(outputs('Compose'),add(item(),1),1),'ﾟ')),concat(substring(outputs('Compose'),item(),1),substring(outputs('Compose'),add(item(),1),1)),substring(outputs('Compose'),item(),1))
```

長い。見にくい。これがローコードだといえるかは甚だ微妙です。
やっていることは、

>range(0,文字数-1)を範囲として、
```range(0,sub(length(outputs('Compose')),1)```
>一文字ずれた場所の文字列が濁点と等しいまたは半濁点と等しいならば
```if(or(equals(substring(outputs('Compose'),add(item(),1),1),'ﾞ'),equals(substring(outputs('Compose'),add(item(),1),1),'ﾟ'))```
>substringした文字とその次の文字を結合
```concat(substring(outputs('Compose'),item(),1),substring(outputs('Compose'),add(item(),1),1))```
>そうでなければsubstringした文字をそのままにする
```substring(outputs('Compose'),item(),1))```


という表現です。もう一つのMapにはitem()をいれて行番号を振っています。
これにより以下のような配列が取得できます。

例：入力がｶﾞﾎﾟﾄの場合 (文字数5)

```out.json
[
  {
    "chr": "ｶﾞ",
    "rownum": 0
  },
  {
    "chr": "ﾞ",
    "rownum": 1
  },
  {
    "chr": "ﾎﾟ",
    "rownum": 2
  },
  {
    "chr": "ﾟ",
    "rownum": 3
  }
]
```
すでにお気づきかと思いますが、1文字かけています。これはSelectの範囲を**文字数-1**としたためです(こうしないとエラーになる)
1文字かけた分はunion関数でくっつけてあげます。

```
union(body('Select_2'),json(concat('[{"rownum":',sub(length(outputs('Compose')),1),',"chr":"',substring(outputs('Compose'),sub(length(outputs('Compose')),1),1),'"}]')))
```
ステップ数を減らすために手打ちでjsonつくったらこんなことになりました。。もう少しいい手がありそう。

##Filter
最後はFilterです。
ここはFliterが複数条件をポチポチでサポートしてくれていないので、詳細モードで編集します。

```
@not(or(equals(item()?['chr'], 'ﾞ'),equals(item()?['chr'], 'ﾟ')))
```
最初に書いた通り、濁点か半濁点以外を抽出する条件です。


## できあがり！
ここまででやっと半角カナを濁音・半濁音含め、1文字ずつに分割できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/83e9fc98-a2e2-35fb-5a1e-1d1fed754400.png)
あとは全角半角マッピング用のJSONとぶつけてやれば完了です。

# 全角半角カナ変換
ここまでで、以下のような配列が得られました。

**["ｶﾞ", "ﾎﾟ", "ﾄ"]**
あとはこれに対応する文字列をLookUpで見つけてあげれば完成です。
ここではLookUpの番外編で紹介した、[Compose json dictionaryの方法](https://qiita.com/h-nagao/items/25fb21d7133e25c07a34)を利用します。
用意するDictionaryも、Githubにおいてあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/79f5b596-2a9d-8231-7791-b3014b609b7a.png)
カタカナは網羅しているはず。
文字の配列に対して、LookUpして再度文字の配列を作る場合にはSelectアクションを利用します。
余計なif文が入っていますが、これは入力が全角半角混在だった場合の想定です。
もし半角で固定されているのであれば、

```
outputs('HalfFullDict')?[concat('halfWidth_',item())]
```
これで対応する全角文字を取得できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/a685ecbd-8c2e-e538-584c-4d028068c8ba.png)

最後、文字の配列を結合するときはJoinのアクションで、*null*で結合します。
注意しないといけないのは、nullって文字を入れてしまうと、ガnullポnullトになっちゃうので、数式からnullを入れてあげてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/c71c27f0-6664-32cf-7946-5f2da2f5a837.png)


# 実行結果
無事半角から全角への変換ができました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/f5f93474-da38-a9bf-2245-212506afc888.png)

ここでやったような数式をガンガン使うFlowが、ローコードだと言う気はありません。
もう少しローコードな感じで、Apply for eachつかったり、IFのアクション使ったりでできる気もします。
とはいえ、それを何件も処理することを考えると、なるべくコアのロジックは時間かからないようにしたいですよね。
そこらへんのバランスをとりながら、組んでいければいいのかなと思います。


あとはサンプルのFlowもありますので、実際にご自身のFlowで使う場合は、数式をコピペして、変数名変えたりして活用してみてください。