#76　PHPでネストのある配列の値を検索する<br>〜array_reduce, array_column, array_walk の比較

## はじめに
前回記事の導入で`array_reduce()`を使ってネストのある配列の値を検索する処理について調べた旨を記載したところ、「その処理なら`array_reduce()`よりも`array_column()`と`array_filter()`を併用する方法や`array_walk()`を用いた方が良いと思う」とのアドバイスをいただきましたので、今回はこの3パターンで記述した場合について比較していこうと思います。
<br>
それぞれの関数の動きについて簡単に確認した後、パターン別に実装してみた内容を見ていきましょう。

#### 本記事に関連するブログ記事

- 前回のブログ：[リスト内包表記を使ってネストのある配列の値を検索する](https://www.nxted.co.jp/blog/blog_detail?id=73)

#### やりたいこと
前回同様、ネストのある配列で`user`キーの中にある`id`の値で検索し、該当する要素の`mail`の値を取得したいと思います。
わかりやすいように、配列も前回と同じ構成のものを使用します。

```php
// ※前回記事から流用
$array = [
    [
        "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" => "保元の乱"
        ]
    ]
];
```

## 各関数を利用した実装例
### array_reduce()
第一引数で指定した配列の各要素に対して、第二引数で指定したcallback関数を実行した結果をひとつにまとめて返却します。
第三引数のinitialはオプションで、処理の最初で使用／結果が空の場合に返却する値を設定することができます。

<br>

構文：

```php
array_reduce(array, callback関数, initial)
```

実装例：

```php
<?php
// $arrayの内容は上記参照

$userId = 01;　// 検索したい値

echo array_reduce($array,"arrayReduce", $userId);

function arrayReduce($carry, $item) {
    if($carry == $item["user"]["id"]) {
        return $item["mail"];
    }
    return $carry;
}
?>
// echo -> masakado-taira@ex.com
```

検索したい値を第三引数として渡し、一致した要素の`mail`の値をcallback関数の実行結果としてreturnさせました。
上記の実装では検索値と一致した時点で保持される実行結果が`mail`値になるので、検索結果として戻り値が複数であることを期待する場合には不向きかと思います。

<br>

ちなみに、無名関数を使用した記述だと以下のようになります。

```php
<?php
// $arrayの内容は上記を参照

$userId = 01;　// 検索したい値

echo array_reduce($array,function($carry, $item){
    if($carry == $item["user"]["id"]) {
        return $item["mail"];
    }
    return $carry;
}, $userId);
?>
// echo -> masakado-taira@ex.com
```

以降の解説では、無名関数を用いた実装を中心に紹介していきたいと思います。

<br>

### array_column() + array_filter()
**array_column()**
第一引数で指定した多次元配列から、第二引数で指定したカラムに一致した値を配列に格納して返却します。
第二引数はカラムの番号を示す整数値の他、連想配列のキー名やプロパティ名で指定することが可能です。
第三引数はオプションで、渡した配列中から指定されたカラムの値を戻り値のキー名としてキャストさせることができます。

<br>

構文：

```php
array_column(array, column_key, index_key)
```

<br>

**array_filter()**
第二引数で指定したcallback関数の結果がtrueの場合、第一引数で指定した配列の現在の要素の値を結果配列に格納することで、渡した配列の要素をフィルタリングします。
第三引数は引数としてcallback関数に渡す要素（キーのみ／キーと値）示すフラグを指定します。デフォルトは0で、値のみをcallback関数に渡します。

<br>

構文：

```php
array_filter(array, callback関数, mode)
```

実装例：

```php
<?php
// $arrayの内容は上記を参照

$userId = 01;　// 検索したい値

echo array_column(array_filter($array, function($value) use ($userId) {
    if($userId == $value["user"]["id"]) {
        return true;
    }
    return false;
}), "mail")[0];
?>
// echo -> masakado-taira@ex.com
```

検索値と一致する要素でフィルタリングした配列を第一引数とする`array_column()`にて、検索結果を配列の形で返却します。
戻り値が配列であるため検索値が一意である必要はなく、例えば「`sendMailFlg`キーが1であるメールアドレスを取得する」といった処理も可能です。

### array_walk()
第一引数に指定した配列のすべての要素に対して、第二引数で指定したユーザー定義のcallback関数を適用します。
第三引数はオプションで、指定するとcallback関数の第三引数として渡すことができます。
戻り値はboolで、渡した配列の要素に対して追加・削除など構造を変化させることはできません。

<br>

構文：

```php
array_walk(array, callback関数(array.value, array.key), arg) 
```

実装例：

```php
<?php
// $arrayの内容は上記を参照

$userId = 01;　// 検索したい値

array_walk($array, function($value, $key, $arg){
    if ($arg == $value["user"]["id"]) {
        echo $value["mail"];
    }
}, $userId);
?>
// echo -> masakado-taira@ex.com
```

`array_walk()`の第三引数として検索値を渡すことで、callback関数の引数としても使用することができます。
ただし、`array_walk()`自体の戻り値はboolのため、条件に一致する値を抽出して新たに配列を作成する、という利用の仕方には向いていないようです。

## おわりに
今回は期待する処理の実装を`array_reduce()`、`array_column()`、`array_filter()`、`array_walk()`をそれぞれ使用した場合について、簡単な解説と共に実装例を取り上げてみました。

<br>

記事を書くきっかけにもなった、「`array_reduce()`よりも、"（一意の値を持つ）任意のキーの値を検索し、一致した要素の別のキーの値を取り出す"処理に向いたarray関数がある」というお話も、実際に実装してみることでより理解することができたと感じています。

<br>

また、`array_filter()`でスコープ外の変数を渡す方法として、`use`を使用するものがあるということを知りました。
個人的に、関数のスコープ周りの理解が浅いということが実感できたので、これを機会に知識を増やしていこうと思います。

<br>

今回はそれぞれの関数の解説が中心になってしまったので、機会があれば、処理速度的な観点からの比較もしてみたいです。

<br>

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

<br>

参考：

 - https://www.php.net/manual/ja/function.array-reduce.php
 - https://www.php.net/manual/ja/function.array-column.php
 - https://www.php.net/manual/ja/function.array-filter.php
 - https://www.php.net/manual/ja/function.array-walk.php
 - https://qiita.com/skgt123/questions/8972ecd2845c35c0ad04
 - https://zenn.dev/shink32/articles/cc6784251cf2c7
 - https://magazine.techacademy.jp/magazine/19668