> laravel-collections

Full method API, decision guides, pitfalls, and patterns for Laravel's Collection and LazyCollection. Use when choosing the right collection method, writing or reviewing collection pipelines, or working with large datasets via cursor/lazy. Triggers on collect(), Collection, LazyCollection, map, filter, reduce, pluck, groupBy, each, cursor, or fluent array transformations in Laravel.

fetch
$curl "https://skillshub.wtf/riasvdv/skills/laravel-collections?format=md"
SKILL.mdlaravel-collections

Laravel Collections

Quick Decision Guide

"Which method do I use?"

Find items:

NeedMethod
First item matching conditionfirst(fn) or firstWhere('key', 'value')
First item or throwfirstOrFail(fn)
Exactly one match or throwsole(fn)
Check value existscontains('value') or contains('key', 'value')
Check key existshas('key')
All items match conditionevery(fn)

Transform items:

NeedMethod
Transform each itemmap(fn)
Transform + flatten one levelflatMap(fn)
Extract one fieldpluck('name')
Extract field, keyed by anotherpluck('name', 'id')
Select multiple fieldsselect(['id', 'name'])
Change keysmapWithKeys(fn) or keyBy('id')
Map into class instancesmapInto(DTO::class)

Filter items:

NeedMethod
Keep matching itemsfilter(fn)
Remove matching itemsreject(fn)
Filter by field valuewhere('status', 'active')
Filter by field in listwhereIn('status', ['active', 'pending'])
Keep specific keys onlyonly(['id', 'name'])
Remove specific keysexcept(['password'])
Remove duplicatesunique('email')

Group & organize:

NeedMethod
Group by fieldgroupBy('category')
Group with custom key+valuemapToGroups(fn)
Split pass/failpartition(fn)
Key by field (1 item per key)keyBy('id')
Split into chunkschunk(100)

Aggregate:

NeedMethod
Sum valuessum('price')
Averageavg('score')
Min/Maxmin('price') / max('price')
Reduce to single valuereduce(fn, $initial)
Count occurrencescountBy('type')
Percentage matchingpercentage(fn)

Combine collections:

NeedMethod
Merge (overwrites string keys)merge($other)
Append values (re-indexes)concat($other)
Keep only shared valuesintersect($other)
Get values not in otherdiff($other)
Pair items by indexzip($other)

Mutability Rules

Immutable (return new collection) - almost everything: map, filter, reject, sort, merge, pluck, unique, ...

Mutating (modify in place, return $this) - memorize these: transform, push, prepend, put, pull, pop, shift, splice, forget

// WRONG mental model:
$filtered = $collection->push('item'); // $collection IS modified

// Correct approach when you want immutability:
$new = $collection->concat(['item']); // $collection is unchanged

Common Pitfalls

1. each() vs map() vs transform()

// each(): Side-effects, returns ORIGINAL collection
$users->each(fn($u) => $u->notify(new Welcome));

// map(): Transformation, returns NEW collection (original unchanged)
$names = $users->map(fn($u) => $u->name);

// transform(): Transformation, MUTATES original collection
$users->transform(fn($u) => $u->name); // $users is now a collection of names!

2. pluck() key overwrites

When using pluck('name', 'id'), duplicate keys silently overwrite:

collect([
    ['id' => 1, 'name' => 'Alice'],
    ['id' => 1, 'name' => 'Bob'],   // Overwrites Alice
])->pluck('name', 'id');
// [1 => 'Bob']  -- Alice is lost!

Use groupBy if duplicates are expected.

3. filter() without callback removes all falsy values

collect([0, 1, '', 'hello', null, false, []])->filter()->all();
// [1, 'hello'] -- 0, '', null, false, [] are ALL removed

Use whereNotNull() if you only want to remove nulls.

4. N+1 queries with collection iteration

// BAD: N+1 queries
$users->each(fn($user) => $user->posts->count());

// GOOD: Eager load first
$users->load('posts');
$users->each(fn($user) => $user->posts->count());

5. Memory with large datasets

// BAD: Loads all users into memory
User::all()->each(fn($u) => process($u));

// GOOD: Constant memory with cursor
User::cursor()->each(fn($u) => process($u));

// GOOD: Chunked processing
User::chunk(1000, fn($users) => $users->each(fn($u) => process($u)));

6. sort() preserves keys

collect([3, 1, 2])->sort()->all();
// [1 => 1, 2 => 2, 0 => 3] -- keys preserved!

collect([3, 1, 2])->sort()->values()->all();
// [1, 2, 3] -- use values() to re-index

Higher-Order Messages

Proxy pattern for clean, concise collection operations:

// Instead of:
$users->map(function ($user) { return $user->name; });

// Write:
$users->map->name;

// Works with methods too:
$users->each->markAsVip();
$users->sum->votes;
$users->filter->isAdmin();
$users->sortBy->name;
$users->reject->isBlocked();
$users->groupBy->department;

Supported on: average, avg, contains, each, every, filter, first, flatMap, groupBy, keyBy, map, max, min, partition, reject, skipUntil, skipWhile, some, sortBy, sortByDesc, sum, takeUntil, takeWhile, unique.

Extending with Macros

Register custom collection methods (typically in a service provider's boot method):

use Illuminate\Support\Collection;

Collection::macro('toUpper', function () {
    return $this->map(fn($value) => strtoupper($value));
});

// Usage
collect(['foo', 'bar'])->toUpper(); // ['FOO', 'BAR']

Reference Files

┌ stats

installs/wk0
░░░░░░░░░░
github stars3
░░░░░░░░░
first seenMar 17, 2026
└────────────

┌ repo

riasvdv/skills
by riasvdv
└────────────

┌ tags

└────────────