Slow `where()` query in Laravel collections

Laravel collections1 are the greatest thing since sliced bread and I use them a lot to transform API responses. There is even an adaption in Java Script called collect.js2.

Most of the time collections are fast until they are not, for example you query a large set with where() inside a map() operation that runs over 200 items.

$collection = collect([/* complex object with 200 nodes */]);
$reference = collect([/* another complex object with 300 nodes */]);

$result = $collection->map(function($item) use ($reference) {

  $item['reference'] = $reference->where('collectionId', $item['id'])->first();

  return $item;
});

This is nice to read and noticeably slow!

As soon as you rewrite this and use indexed arrays by adding keyBy() and retrive the object with get() things speed up significantly.

$collection = collect([/* complex object with 200 nodes */]);
$reference = collect([/* another complex object with 300 nodes */])->keyBy('collectionId');

$result = $collection->map(function($item) use ($reference) {

  $item['reference'] = $reference->get($item['id']);

  return $item;
});

💥 down from 3 seconds to 300 milliseconds.

Bonus Tip

To turn an array into an indexed array with keys, instead of

$obj = [
  ['id' => 'abc', 'letter' => 'a'],
  ['id' => 'def', 'letter' => 'b'],
  ['id' => 'ghi', 'letter' => 'c']
];

$obj = collect($obj)->keyBy()->all();

just do this:

$obj = array_column($obj, null, 'id');

Credit

$due = 'https://laracasts.com/discuss/channels/laravel/working-on-collections-is-so-slow?page=1#reply=108063';
$credit->where('credit', $due);