#70　リスト内包表記を使ってネストのある配列の値を検索する

## はじめに
以前phpでarray_reduce()を使ってネストのある配列の値を検索する処理を調べる機会があり、他の言語、例えばpythonを使用するとどのように書けるのか気になったので、調べてみました。

<br>

すると、pythonではfor文の代わりにリスト内包表記というものを使うと、よりシンプルに書けるということがわかりました。
今回はそんなリスト内包表記について、for文で記述した場合と比較しつつ備忘録としてまとめていきたいと思います。


### やりたいこと
ネストのある元配列を一意の値で検索して、同じの要素の中で深さの違うキーの値を取り出したいと思います。

文章だけだとわかりにくいですね。

<br>

以下のようにネストされた配列があるとします。

```python
arrays = [
    {
        "id": 930,
        "mail": "suga59@ex.com",
        "sendMailFlg": "1",
        "user": {
            "id": 02,
            "name": "菅原道真",
            "era": "平安時代",
            "event": "清涼殿落雷事件"
        }
    },
    {
        "id": 935,
        "mail": "masakado-taira@ex.com",
        "sendMailFlg": "1",
        "user": {
            "id": 01,
            "name": "平将門",
            "era": "平安時代",
            "event": "平将門の乱"
        }
    },
    {
        "id": 1156,
        "mail": "sutoku-4767@ex.com",
        "sendMailFlg": "0",
        "user": {
            "id": 03,
            "name": "崇徳天皇",
            "era": "平安時代",
            "event": "保元の乱"
        }
    }
]
```

この時「`user`キーの中にある`id`の値で検索し、該当する要素の`mail`の値を配列で取得する」という処理がしたいとしま
す。
イメージはできたでしょうか？

<br>

まずはこの処理を、for文を使用して作成してみましょう。

## 実装

### for文を使用した例

```python
userId = 02  # 検索したい値

mailList = []
for array in arrays:
    if array.get("user")["userId"] == userId:
        mailList.append(array.get("mail"))

print(mailList)
# ['suga59@ex.com']
```

空の配列を用意して、for文で各要素の`user`キーの値の中の`id`と検索値が一致するかを判定。trueであれば用意した配列の中にその要素の`mail`キーの値を追加する、という内容ですね。

<br>

pythonで記述する場合、リストから新たなリストを生成する際に使用する「リスト内包表記」を用いることで、上記の処理を下のように1行で表すことができます。

### リスト内包表記を使用した例

```python
userId = 02  # 検索したい値

mailList = [array.get("mail") for array in arrays if array.get("user")["userId"] == userId]

print(mailList)
# ['suga59@ex.com']
```

for文と比べて、シンプルな記述になったのがわかりますね。
次では、リスト内包表記の基本的な内容についてまとめていきたいと思います。

## リスト内包表記とは
基本型は以下の通りです。

```
[式 for 任意の変数名 in イテラブルオブジェクト]
```

実装例で考えると「配列の各要素の`mail`の値を`mailList`に追加する」までの処理がこれにあたります。

```python
mailList = [array.get("mail") for array in arrays]

print(mailList)
# ['suga59@ex.com', 'masakado-taira@ex.com', 'sutoku-4767@ex.com']
```

実装例ではこれにif文を追加しているので、型としては

```
[式 for 任意の変数名 in イテラブルオブジェクト if 条件式]
```

となります。
ちなみに型の「式」の箇所には三項演算子を用いることも可能なので、「`user`キーの値の中にある`id`の値が検索値と一致する要素であれば`mail`の値を`mailList`に追加し、そうでなければ空文字を追加する」なんて処理も1行で表すことができます。
```python
# 型
[真のときの値 if 条件式 else 偽のときの値 for 任意の変数名 in イテラブルオブジェクト]

userId = 02  # 検索したい値
mailList = [array.get("mail") if array.get("user")["id"] == userId else "" for array in arrays]

print(mailList)
# ['suga59@ex.com', '', '']
```

### 部分一致で検索
完全一致による検索だけでなく、部分一致での検索も可能です。
せっかくなので「`user`キーの値の中にある`event`の値に「乱」が含まれている要素の`mail`の値を`mailList`に追加する」処理で確認してみましょう。

```python
# 型
[式 for 任意の変数名 in イテラブルオブジェクト if 検索したい文字列 in 検索対象となる文字列]

mailList = [array.get("mail") for array in arrays if "乱" in array.get("user")["event"]]

print(mailList)
# ['masakado-taira@ex.com', 'sutoku-4767@ex.com']
```

`検索したい文字列 not in 検索対象となる文字列`にすることで、検索したい文字列が含まれていない要素を取得することも可能です。

```python
# 型
[式 for 任意の変数名 in イテラブルオブジェクト if 検索したい文字列 not in 検索対象となる文字列]

mailList = [array.get("mail") for array in arrays if "乱" not in array.get("user")["event"]]

print(mailList)
# ['suga59@ex.com']
```

## おわりに
いかがでしたでしょうか？
リスト内包表記というものを初めて知り、またphpでは見ない表記なので、今回記事にするにあたり調べていて面白かったです。

<br>

今回はリスト内包表記を用いた値の検索について取り上げましたが、正規表現や複数条件、値からキーを検索することも可能とのことです。
興味のある方はぜひ調べてみてください。

<br>

最後まで閲覧いただきありがとうございます。

<br><br>

参考：

 - https://note.nkmk.me/python-dict-list-values/
 - https://note.nkmk.me/python-dict-get-key-from-value/
 - https://note.nkmk.me/python-list-comprehension/
 - https://note.nkmk.me/python-list-str-select-replace/#in