👨‍🎓

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

に公開

はじめに

前回記事の導入でarray_reduce()を使ってネストのある配列の値を検索する処理について調べた旨を記載したところ、「その処理ならarray_reduce()よりもarray_column()array_filter()を併用する方法やarray_walk()を用いた方が良いと思う」とのアドバイスをいただきましたので、今回はこの3パターンで記述した場合について比較していこうと思います。


それぞれの関数の動きについて簡単に確認した後、パターン別に実装してみた内容を見ていきましょう。

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

やりたいこと

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

// ※前回記事から流用
$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はオプションで、処理の最初で使用/結果が空の場合に返却する値を設定することができます。


構文:

array_reduce(array, callback関数, initial)

実装例:

<?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値になるので、検索結果として戻り値が複数であることを期待する場合には不向きかと思います。


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

<?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

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


array_column() + array_filter()

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


構文:

array_column(array, column_key, index_key)


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


構文:

array_filter(array, callback関数, mode)

実装例:

<?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で、渡した配列の要素に対して追加・削除など構造を変化させることはできません。


構文:

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

実装例:

<?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()をそれぞれ使用した場合について、簡単な解説と共に実装例を取り上げてみました。


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


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


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


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


参考:

Discussion