こんにちは!アテナのエンジニアkです。

私が現在携わっているプロジェクトではPHPのLaravelフレームワークを使用しています。

今回はLaravelの特長的な機能として備わっているCollectionでエラーがでて落とし穴に落ちた話をしたいと思います!

LaravelのCollectionクラスは、データのフィルタリングや変換などの操作を行う際に非常に便利です。
しかし、filterメソッドを使用すると、意図しない結果が返されることがあります。具体的には、filterメソッドを実行した際に、元の配列のキーが保持されることがあります。

この現象は、LaravelのCollectionが内部的に元の配列のキーを保持しているために起こります。filterメソッドは、コールバック関数を適用して要素をフィルタリングする際に、元のキーを保持したまま新しい配列を作成します。これは、連想配列の場合に特に顕著です。

例えば、以下のような連想配列があるとします。

$users = [
    {
        'id' => 1,
        'name' => 'Michael',
        'number' => 23,
    },
    {
        'id' => 1,
        'name' => 'Kobe',
        'number' => 8,
    },
    {
        'id' => 1,
        'name' => 'Kyrie',
        'number' => 11,
    },
];

この配列をfilterメソッドでフィルタリングすると、次のような結果が得られます。

$filteredData = collect($users )->filter(function ($user) {
    return $user->number = 11;
})->map(function ($user) {
    return [
        'name' => $user->name
    ];
});

// $filteredData->toArray()すると
// [
//     2 => [
//          'name' => 'Kyrie'
//     ];
// ]

なぜかkeyにindexが設定されています。
また、filterでCollectionのデータが除外されない場合はkeyにindexは設定されません。
他の言語の一般的な配列のfilterメソッドとは異なる挙動です。

この問題を回避するためには、valuesメソッドを使用して値だけを取り出すことができます。

$filteredData = collect($users )->filter(function ($user) {
    return $user->number = 11;
})->values() // ここを追加
->map(function ($user) {
    return [
        'name' => $user->name
    ];
});

// $filteredData->toArray()すると
// [
//     [
//          'name' => 'Kyrie'
//     ];
// ]

これでfilterしてもkeyにindexが設定されない形でデータが取得できます。

簡単な例ですがこんな感じで、エラーにぶち当たってからプログラムの内部仕様などを知る機会があります。

日々トライアンドエラーで成長していきましょう!