読者です 読者をやめる 読者になる 読者になる

ユアマイスター株式会社エンジニアブログ

ユアマイスター株式会社のエンジニアが日々徒然。

array_walk_recursiveで配列の要素が配列でもコールバックを適用したい

こんにちは、エンジニアインターンの國丸です。

今日はphparray_walk_recursive関数で配列の要素が配列でもコールバックを適用したいというシーンがあったので、解決方法を書きたいと思います。

配列に再帰的にコールバックを適用しない場合はarray_map

配列の要素が配列のときコールバックを適用しない場合はarray_walk_recursiveを使えば良いです。

今回の場合は

再帰的に配列の要素が配列でもコールバックを適用したいので、再帰関数でreturn array_map()することになります。

以下の例はユアマイスターの開発中に出てきたもので、

与えられる配列がCategoryオブジェクトの配列で、

その各々のCategoryオブジェクトのchildrenプロパティがCategoryオブジェクトの配列を持つというネスト構造になっています。

array(4) {
  [0]=>
  object(Category) {
    ["id"]=>
    int(1)
    ["name"]=>
    string(27) "○○クリーニング"
    ["children"]=>
    array(20) {
      [0]=>
      object(Category) {
        ["id"]=>
        int(2)
        ["name"]=>
        string(30) "●●クリーニング"
        ["children"]=>
        array(2) {
          [0]=>
          object(Category) {
            ["id"]=>
            int(23)
            ["name"]=>
            string(9) "▲▲"
            ["children"]=>
            array(0) {
            }

\\ ...

そこから最下層のカテゴリのidをキー、先祖カテゴリとそのカテゴリのnameを全て繋げたものを値として配列を作ります。($names)

$names = [];
$format = function ($ancestor, $children) use (&$format, &$names) {
    \\ 最下層の場合配列に追加
    if (empty($children)) {
        $names[$ancestor['id']] = $ancestor['name'];
        return;
    }

    \\ 要素:親までの先祖カテゴリの名前を繋げたもの・親カテゴリのid
    $newAncestor = [];
    \\ 要素:子カテゴリの配列
    $newChildren = [];
    foreach ($children as $child) {
        $newAncestor[] = [
            'id' => $child['id'],
            'name' => (isset($ancestor['name']) ? $ancestor['name'] : '') . ' ' . $child['name'],
        ];
        $newChildren[] = isset($child['children']) ? $child['children'] : [];
    }

    return array_map($format, $newAncestor, $newChildren);
};

$format([], $categories);

これを並列処理でやって再帰1回あたりの時間を固定すると、木の中の同じ深さのノードが同じタイミングでコールバックで処理され、その数がどんどん増加していく…想像すると楽しいですね。皆さんのお役に立てたら幸いです。