Edited at

EloquentでmapWithKeysでintはkeyに指定できない

More than 1 year has passed since last update.

注意:Laravel5.3での話です!(5.4以降は修正されております)

タイトルのままですが、CollectionのmapWithKeysメソッドを使用する際、returnする配列のkeyにintを指定するとkeyが自動的に0,1,2...と設定されてしまうバグがあって困ってました。

他に困る方がいらっしゃった時のために記事にして残しておこうと思います。


mapWithKeysの説明

この記事にたどり着く方にmapWithKeysの挙動について説明する必要もないかと思うのですが、念のため記載いたします。

LaravelのドキュメントのmapWithKeysをところ(https://readouble.com/laravel/5.3/ja/collections.html#method-mapwithkeys )を見てみると

mapWithKeysメソッドはコレクション全体を反復処理し、指定したコールバックへ各値を渡します。

コールバックからキー/値ペアを一つ含む連想配列を返してください。

とあります。

上の説明で十分かと思いますが、PHPの標準関数的に言うとarray_mapでkeyとvalue同時に指定できるよー、な感じでしょうか?


コードの内容とバグの詳細

以下のようなコードを書いた際に


$collection = new Collection();
$collection = collect([
['id' => '1', 'name' => 'Yamada'],
['id' => '2', 'name' => 'Suzuki'],
['id' => '3', 'name' => 'Satou']
]);

$collection = $collection->mapWithKeys(function($item) {
return [$item['id'] => $item['name']];
});

dd($collection->toArray());

予想される出力は


array(3) {
[1]=>
string(6) "Yamada"
[2]=>
string(6) "Suzuki"
[3]=>
string(5) "Satou"
}

となります。

ですが実際に出力されるものはなぜか配列のkeyを0から順番に振り直してしまうので


array(3) {
[0]=>
string(6) "Yamada"
[1]=>
string(6) "Suzuki"
[2]=>
string(5) "Satou"
}

となってしまいます。


解決策

一番簡単な方法はversionを5.4以上にあげることだと思います。が、私を含めて、何らかの理由があってversionを上げれない方もいらっしゃるかと思います。

そのような方は以下のコードを追記してください。


collect()->macro('mapWithKeys_v2', function ($callback) {

$result = [];

foreach ($this->items as $key => $value) {
$assoc = $callback($value, $key);

foreach ($assoc as $mapKey => $mapValue) {
$result[$mapKey] = $mapValue;
}
}

return new static($result);
});

Eloquentに詳しい方でしたら解説は不要かと思いますが、macroメソッドで新たにメソッドを登録しています。

以降は


$collection = $collection->mapWithKeys_v2(function($item) {
return [$item['id'] => $item['name']];
});

のように呼び出したら予想している通りの挙動として使用できます。


最後に

って、githubのissueに全部書いてありました!

https://github.com/laravel/framework/issues/15409