🕵🏾

【Laravel9】Collectionクラスの中身を見る~flattenメソッド編~

2023/02/02に公開

はじめに

配列を操作するためのラッパークラスであるIlluminate\Support\CollectionクラスにはLaravel9では139個ものメソッドがあります。
Laravelを使用していたら見かけるであろう、map()やpluck()、merge()はCollectionクラスのメソッドです。

公式ドキュメントも貼っておきます。

https://readouble.com/laravel/9.x/ja/collections.html

今回はflattenメソッドが何をしているのか見ていきます。

環境

  • Windows11(WSL2)
  • PHP 8.2.1
  • Laravel 9.50.1
  • sail 2.6.1

flattenメソッドとは

Collectionクラスのflattenメソッドは多次元のコレクションを1次元化します。
また、引数$depthでどのネストまでを対象にするかを指定することができます。

動作確認

簡単なプログラムでflattenメソッドの動きを確認します。

$depth指定無し

まずはdepthを指定しない場合

$collection = collect([
    '料理' => [
        '中華' => [
            '餃子' => [
                'シソ餃子',
                '肉餃子',
            ],
            '天津飯'
        ],
        'イタリアン' => [
            'ピザ',
            'リゾット'
        ],
        '日本' => [
            '味噌汁',
            '寿司',
        ],
        '台湾' => 'ルーローハン',
    ],
]);

$result = $collection->flatten();

dd($result);
Illuminate\Support\Collection {#280 ▼
  #items: array:8 [▼
    0 => "シソ餃子"
    1 => "肉餃子"
    2 => "天津飯"
    3 => "ピザ"
    4 => "リゾット"
    5 => "味噌汁"
    6 => "寿司"
    7 => "ルーローハン"
  ]
  #escapeWhenCastingToString: false
}

$depth指定有り

depth()の引数に2を指定します。

$collection = collect([
    '料理' => [
        '中華' => [
            '餃子' => [
                'シソ餃子',
                '肉餃子',
            ],
            '天津飯'
        ],
        'イタリアン' => [
            'ピザ',
            'リゾット'
        ],
        '日本' => [
            '味噌汁',
            '寿司',
        ],
        '台湾' => 'ルーローハン',
    ],
]);

$result = $collection->flatten(2);

dd($result);
Illuminate\Support\Collection {#275 ▼
  #items: array:7 [▼
    0 => array:2 [▼
      0 => "シソ餃子"
      1 => "肉餃子"
    ]
    1 => "天津飯"
    2 => "ピザ"
    3 => "リゾット"
    4 => "味噌汁"
    5 => "寿司"
    6 => "ルーローハン"
  ]
  #escapeWhenCastingToString: false
}

結果は上記のようになりました。
2階層までを1次元化するのでシソ餃子、肉餃子以外が1次元になります。

flattenメソッドの中身

Illuminate\Support\Collection::flatten

Collectionクラスのflattenメソッドは以下のようになっています。

public function flatten($depth = INF)
{
    return new static(Arr::flatten($this->items, $depth));
}

引数depthのデフォルト値はINFという定数が指定されています。INFはPHPの定義済み定数で、意味的にはinfinity(無限)です。

new staticなのでflattenメソッドが呼び出されたときに、引数のArr::flattenをインスタンス化してreturnしています。

さらにIlluminate\Support\Arrクラスのflattenメソッドを追ってみます。

Illuminate\Support\Arr::flatten

Arrクラスのflattenメソッドは以下の通りとなります。

public static function flatten($array, $depth = INF)
{
    $result = [];

    foreach ($array as $item) {
        $item = $item instanceof Collection ? $item->all() : $item; //①

        if (! is_array($item)) {
             $result[] = $item; //②
        } else {
            $values = $depth === 1 //③
                ? array_values($item)
                : static::flatten($item, $depth - 1);

            foreach ($values as $value) {
                $result[] = $value;
            }
        }
    }

    return $result;
}

①多次元のコレクションにも対応させるための処理です。
三項演算子の$item instanceof Collectionで処理を分けているため以下のようなコレクションでも1次元化できます。

$collection = collect([
    collect(['test1','test2']),
    collect(['test3','test4']),
]);

$result = $collection->flatten();

dd($result);
Illuminate\Support\Collection {#276 ▼
  #items: array:4 [▼
    0 => "test1"
    1 => "test2"
    2 => "test3"
    3 => "test4"
  ]
  #escapeWhenCastingToString: false
}

②$itemが配列でない場合はそのまま値を結果にセットします
最初の例でいうと、'台湾' => 'ルーローハン',のルーローハンが該当します。

③$itemが配列且つ$depthが1のときはPHP組み込み関数のarray_valuesで数字添え字付き配列を結果にセットするようになっています。

$depthが1より大きい又はデフォルト値のときはstaticで自身の関数を呼び出して再帰的に処理して、
static::flatten($item, $depth - 1);と$depthから-1していてdepthが1になるまで1次元化を繰り返します。
デフォルト値のときは全て1次元化されるまで繰り返します。

まとめ

Collectionクラスのflattenメソッドは、$depthがデフォルト値であれば全て1次元化し、$depthに指定があれば、指定の階層まで再帰的に1次元化することが分かりました。

Discussion