One of ListCollection’s most powerful features is the ability to chain multiple operations together. Every operation maintains sequential numeric indices, making chaining predictable and safe.
Basic Chaining
ListCollection methods return new ListCollection instances, allowing you to chain operations:
$list = new ListCollection([5, 3, 1, 4, 2, 3, 5]);
$result = $list
->filter(fn (int $v): bool => $v > 1)
->unique()
->sort();
// Result: [0 => 2, 1 => 3, 2 => 4, 3 => 5]
Each operation:
filter() removes values ≤ 1 → [5, 3, 4, 2, 3, 5]
unique() removes duplicates → [5, 3, 4, 2]
sort() sorts ascending → [2, 3, 4, 5]
- Keys are sequential at every step:
[0, 1, 2, 3]
Unlike standard Laravel Collections, ListCollection automatically maintains sequential keys after each operation, eliminating the need to call values() at the end.
Combine filtering, transformation, and sorting:
$list = new ListCollection([
['active' => true, 'name' => 'Charlie', 'score' => 85],
['active' => false, 'name' => 'Alice', 'score' => 92],
['active' => true, 'name' => 'Bob', 'score' => 78],
['active' => true, 'name' => 'Diana', 'score' => 95],
]);
$result = $list
->where('active', true)
->sortByDesc('score')
->pluck('name');
// Result: [0 => 'Diana', 1 => 'Charlie', 2 => 'Bob']
Map and Flatten
Transform elements and flatten the result:
$list = new ListCollection([1, 2, 3]);
$result = $list
->map(fn (int $v): array => [$v, $v * 10])
->flatten();
// Result: [0 => 1, 1 => 10, 2 => 2, 3 => 20, 4 => 3, 5 => 30]
Or use flatMap() as a shortcut:
$list = new ListCollection([1, 2, 3]);
$result = $list->flatMap(fn (int $v): array => [$v, $v * 10]);
// Result: [0 => 1, 1 => 10, 2 => 2, 3 => 20, 4 => 3, 5 => 30]
Build sophisticated data pipelines:
$list = new ListCollection([
['category' => 'A', 'items' => [1, 2, 3]],
['category' => 'B', 'items' => [4, 5]],
['category' => 'A', 'items' => [6]],
]);
$result = $list
->where('category', 'A')
->pluck('items')
->flatten()
->filter(fn (int $v): bool => $v > 2)
->sort();
// Result: [0 => 3, 1 => 6]
Step by step:
- Filter for category ‘A’ → 2 items
- Extract ‘items’ arrays →
[[1, 2, 3], [6]]
- Flatten to single level →
[1, 2, 3, 6]
- Keep values > 2 →
[3, 6]
- Sort ascending →
[3, 6] with keys [0, 1]
Chaining Mutating Operations
Some methods modify the original collection and return $this, allowing continued chaining:
$list = new ListCollection(['a', 'b', 'c']);
$list
->push('d')
->prepend('z')
->forget(2);
// List: [0 => 'z', 1 => 'a', 2 => 'c', 3 => 'd']
Be careful when chaining methods like forget() - indices are re-indexed after each call:$list = new ListCollection(['a', 'b', 'c', 'd']);
$list->forget(1); // removes 'b' → [0 => 'a', 1 => 'c', 2 => 'd']
$list->forget(1); // removes 'c' at NEW index 1 → [0 => 'a', 1 => 'd']
Combine subsetting with transformations:
$list = new ListCollection([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
$result = $list
->skip(2)
->take(5)
->map(fn (int $v): int => $v * 2)
->filter(fn (int $v): bool => $v > 10);
// Result: [0 => 12, 1 => 14]
Breakdown:
- Skip first 2 →
[3, 4, 5, 6, 7, 8, 9, 10]
- Take 5 →
[3, 4, 5, 6, 7]
- Multiply by 2 →
[6, 8, 10, 12, 14]
- Filter > 10 →
[12, 14] with keys [0, 1]
Conditional Processing
Use conditional methods in chains:
$list = new ListCollection([1, 2, 3, 4, 5, 6, 7, 8]);
$result = $list
->skipWhile(fn (int $v): bool => $v < 3)
->takeUntil(fn (int $v): bool => $v > 6)
->map(fn (int $v): int => $v ** 2);
// Result: [0 => 9, 1 => 16, 2 => 25, 3 => 36]
Partition and Process
Split data and process each part:
$list = new ListCollection([1, 2, 3, 4, 5, 6]);
[$even, $odd] = $list->partition(fn (int $v): bool => $v % 2 === 0);
$evenSquared = $even->map(fn (int $v): int => $v ** 2);
$oddDoubled = $odd->map(fn (int $v): int => $v * 2);
// $evenSquared: [0 => 4, 1 => 16, 2 => 36]
// $oddDoubled: [0 => 2, 1 => 6, 2 => 10]
Working with Nested Data
Chain operations on nested structures:
$list = new ListCollection([
['users' => [['name' => 'Alice'], ['name' => 'Bob']]],
['users' => [['name' => 'Charlie']]],
['users' => [['name' => 'Diana'], ['name' => 'Eve']]],
]);
$result = $list
->pluck('users')
->flatten(1)
->pluck('name')
->filter(fn (string $name): bool => strlen($name) > 3)
->sort();
// Result: [0 => 'Alice', 1 => 'Charlie', 2 => 'Diana']
Combining Set Operations
Chain set-based operations:
$list1 = new ListCollection([1, 2, 3, 4, 5]);
$list2 = [2, 4, 6, 8];
$list3 = [4, 5, 6, 7];
$result = $list1
->diff($list2)
->merge($list3)
->unique()
->sort();
// Result: [0 => 1, 1 => 3, 2 => 4, 3 => 5, 4 => 6, 5 => 7]
Step by step:
- Diff removes 2, 4 →
[1, 3, 5]
- Merge adds list3 →
[1, 3, 5, 4, 5, 6, 7]
- Unique removes duplicates →
[1, 3, 5, 4, 6, 7]
- Sort ascending →
[1, 3, 4, 5, 6, 7]
Chunk and Process
Process data in chunks:
$list = new ListCollection(range(1, 10));
$result = $list
->chunk(3)
->map(fn (ListCollection $chunk): int => $chunk->sum())
->filter(fn (int $sum): bool => $sum > 10);
// Result: [0 => 12, 1 => 15, 2 => 24]
Breakdown:
- Chunk into groups of 3 →
[[1,2,3], [4,5,6], [7,8,9], [10]]
- Sum each chunk →
[6, 15, 24, 10]
- Keep sums > 10 →
[15, 24] with keys re-indexed to [0, 1]
Best Practices
Performance: Chain operations efficiently by placing filtering operations early to reduce the dataset size before expensive transformations.
// Good: Filter first, then transform
$result = $list
->filter(fn ($item): bool => $item['active'])
->map(fn ($item): array => $this->expensiveTransform($item));
// Less efficient: Transform everything, then filter
$result = $list
->map(fn ($item): array => $this->expensiveTransform($item))
->filter(fn ($item): bool => $item['active']);
Readability: Break long chains into multiple lines for better readability:
$result = $list
->where('active', true)
->sortByDesc('priority')
->take(10)
->pluck('name');
Type Safety: ListCollection maintains type safety through the chain - every operation returns a ListCollection with sequential keys.