PHPackages                             derheyne/laravel-list - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. derheyne/laravel-list

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

derheyne/laravel-list
=====================

A Laravel Collection wrapper that enforces sequential 0-based keys at all times

1.0.0(3w ago)10[2 PRs](https://github.com/derheyne/laravel-list/pulls)MITPHPPHP ^8.3CI passing

Since Mar 20Pushed 3w agoCompare

[ Source](https://github.com/derheyne/laravel-list)[ Packagist](https://packagist.org/packages/derheyne/laravel-list)[ Docs](https://github.com/dhy/laravel-list)[ GitHub Sponsors]()[ RSS](/packages/derheyne-laravel-list/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (13)Versions (4)Used By (0)

[![Laravel List logo](art/logo.png)](art/logo.png)

 A Laravel Collection that guarantees sequential, 0-based integer keys at all times.

 [ ![Latest Version on Packagist](https://camo.githubusercontent.com/9e55625d2f96612261cb2bf2f4377f46135e535ea0b4f20c902392a346d37655/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6465726865796e652f6c61726176656c2d6c6973742e7376673f7374796c653d666c61742d737175617265) ](https://packagist.org/packages/derheyne/laravel-list) [ ![GitHub Tests Action Status](https://camo.githubusercontent.com/c3815f11fc86b3def873bedae0045c07b9d20e1c64629bc7054b12c963f78c19/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6465726865796e652f6c61726176656c2d6c6973742f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265) ](https://github.com/derheyne/laravel-list/actions?query=workflow%3Arun-tests+branch%3Amain) [ ![Total Downloads](https://camo.githubusercontent.com/cafd8e7536931c5c028285a0e190ae373c07c435f27971a59ea827656b140ec9/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6465726865796e652f6c61726176656c2d6c6973742e7376673f7374796c653d666c61742d737175617265) ](https://packagist.org/packages/derheyne/laravel-list)

Laravel List
============

[](#laravel-list)

`ListCollection` is a drop-in replacement for `Illuminate\Support\Collection` that enforces a single invariant: **keys are always `[0, 1, 2, ...]`**. Every operation that would otherwise leave gaps re-indexes automatically, and operations that fundamentally produce associative keys throw a `BadMethodCallException` instead of silently changing the shape of your data.

```
use dhy\LaravelList\ListCollection;

$list = new ListCollection(['a', 'b', 'c']);

$list->filter(fn ($v) => $v !== 'b')->toJson();
// "[\"a\",\"c\"]"  -- a JSON array, even after filtering
```

Why?
----

[](#why)

Laravel's `Collection` preserves keys through almost every operation. After `filter()`, `sort()`, `unique()`, or `forget()` you are typically left with gaps:

```
$c = collect(['a', 'b', 'c'])->filter(fn ($v) => $v !== 'b');

$c->all();    // [0 => 'a', 2 => 'c']      -- gap at index 1
$c->toJson(); // {"0":"a","2":"c"}         -- JSON object, not array
$c[1];        // null                      -- unexpected
```

This is the right default for a general-purpose collection, but it is the wrong default any time the data is conceptually a list -- an API response, a frontend payload, an indexed sequence, anywhere a "missing" key would be a bug. Defending against it manually means sprinkling `->values()` calls through your code and remembering to do so every time.

`ListCollection` removes that whole class of bug:

```
$list = (new ListCollection(['a', 'b', 'c']))->filter(fn ($v) => $v !== 'b');

$list->all();    // [0 => 'a', 1 => 'c']
$list->toJson(); // ["a","c"]
$list[1];        // 'c'
```

Installation
------------

[](#installation)

```
composer require derheyne/laravel-list
```

There is no service provider, facade, or configuration. Use the class directly wherever you would use a `Collection`.

Usage
-----

[](#usage)

### Creating a list

[](#creating-a-list)

The constructor and all static factories normalise their input to a sequential list. Associative keys, sparse integer keys, and `Collection` inputs are all flattened to `[0, 1, 2, ...]`:

```
use dhy\LaravelList\ListCollection;

(new ListCollection(['a' => 1, 'b' => 2, 'c' => 3]))->all();
// [0 => 1, 1 => 2, 2 => 3]

(new ListCollection(collect([10 => 'x', 20 => 'y'])))->all();
// [0 => 'x', 1 => 'y']

ListCollection::make([1, 2, 3]);
ListCollection::wrap('single value');           // [0 => 'single value']
ListCollection::times(5, fn ($i) => $i * 2);    // [2, 4, 6, 8, 10]
```

The package also ships a global `list_of()` helper, the `ListCollection` analogue of Laravel's `collect()`. It accepts either a single iterable / `Arrayable` / `Collection`, or any number of variadic items:

```
list_of();                 // empty list
list_of([1, 2, 3]);        // [0 => 1, 1 => 2, 2 => 3]
list_of(1, 2, 3);          // [0 => 1, 1 => 2, 2 => 3]
list_of('hello');          // [0 => 'hello']
list_of([1, 2], [3, 4]);   // [0 => [1, 2], 1 => [3, 4]]

list_of(3, 1, 4, 1, 5)
    ->unique()
    ->sort()
    ->all();               // [0 => 1, 1 => 3, 2 => 4, 3 => 5]
```

### Filtering, sorting, deduplicating

[](#filtering-sorting-deduplicating)

These all return a new `ListCollection` with re-indexed keys -- no `->values()` required:

```
$list = new ListCollection([10, 25, 30, 5, 15]);

$list->filter(fn ($v) => $v > 10);  // [0 => 25, 1 => 30, 2 => 15]
$list->reject(fn ($v) => $v > 20);  // [0 => 10, 1 => 5,  2 => 15]
$list->sort();                      // [0 => 5,  1 => 10, 2 => 15, 3 => 25, 4 => 30]
$list->unique();
$list->whereIn(/* … */);
```

### Adding, removing, replacing

[](#adding-removing-replacing)

```
$list = new ListCollection(['a', 'b', 'c', 'd']);

$list->forget(1);            // remove by index, re-indexes:        [a, c, d]
$list->forget([0, 2]);       // remove multiple indices at once
$list->pull(1);              // remove and return the value, re-indexes
$list->prepend('z');         // always adds to the beginning (the $key argument is ignored)
$list->push('x');
$list->shift();
$list->pop();
$list->splice(1, 1, ['X']);  // remove and replace, re-indexes
```

`forget()`, `prepend()`, `transform()`, `pull()`, `push()`, `pop()`, `shift()`, and `splice()` mutate in place. Methods like `filter()`, `map()`, `sort()`, `slice()`, and `concat()` return a new instance. This is the same split as `Illuminate\Support\Collection`.

### Setting values by index

[](#setting-values-by-index)

`offsetSet` is constrained to keep the list contiguous. Valid replacement indices are `0` through `count($list)`; anything else appends:

```
$list = new ListCollection(['a', 'b', 'c']);

$list[] = 'd';        // append:                  [a, b, c, d]
$list[1] = 'B';       // replace index 1:         [a, B, c, d]
$list[4] = 'e';       // index == count, append:  [a, B, c, d, e]
$list[99] = 'z';      // out of range,    append: [a, B, c, d, e, z]
$list[-1] = 'q';      // negative,        append
$list['key'] = 'x';   // string key,      append
```

This means `$list[$i] = $value` is always safe -- it can never punch a hole in the list or create a string key.

### Transformations

[](#transformations)

```
$list = new ListCollection([1, 2, 3]);

$list->map(fn ($v) => $v * 2);          // returns a new ListCollection
$list->transform(fn ($v) => $v * 10);   // mutates in place

$list->slice(1, 2);
$list->reverse();
$list->flatten();
$list->collapse();
$list->flatMap(fn ($v) => [$v, $v]);
$list->merge([4, 5, 6]);
$list->concat([7, 8]);
$list->diff([2]);
$list->intersect([1, 3]);
$list->chunk(2);                                   // ListCollection of ListCollections
$list->partition(fn ($v) => $v > 1);               // [ListCollection, ListCollection]
```

`partition()` and `chunk()` recursively return `ListCollection` instances, so the invariant holds at every level.

### Pulling values

[](#pulling-values)

`pull()` removes an item by index, returns it, and re-indexes the remainder. Defaults can be a value or a closure:

```
$list = new ListCollection(['a', 'b', 'c']);

$list->pull(1);                          // 'b';   list becomes [a, c]
$list->pull(99, 'fallback');             // 'fallback'
$list->pull(99, fn () => expensive());   // closure only runs on miss
```

### `pluck()`

[](#pluck)

`pluck()` works as usual when you only ask for values:

```
$list = new ListCollection([
    ['name' => 'Alice', 'age' => 30],
    ['name' => 'Bob',   'age' => 25],
]);

$list->pluck('name')->all(); // [0 => 'Alice', 1 => 'Bob']
```

Calling `pluck()` with the second `$key` argument throws a `BadMethodCallException`, because using one column as keys inherently produces an associative result.

### JSON serialisation

[](#json-serialisation)

Because keys never have gaps, `toJson()` always produces a JSON array, never an object:

```
$list = new ListCollection([1, 2, 3]);
$list->filter(fn ($v) => $v > 1)->toJson(); // "[2,3]"

// A standard Collection produces an object after the same filter:
collect([1, 2, 3])->filter(fn ($v) => $v > 1)->toJson(); // "{\"1\":2,\"2\":3}"
```

This is the most common reason to reach for `ListCollection` in a Laravel app -- API responses, resource collections, and frontend payloads stay consistent regardless of what filtering or sorting happens upstream.

Blocked methods
---------------

[](#blocked-methods)

The following methods are blocked with a `BadMethodCallException`. Each one inherently produces associative keys, so silently re-indexing would discard information the caller asked for:

MethodWhy it is blocked`flip()`Uses values as keys`combine($values)`Uses current items as keys`groupBy($groupBy)`Groups into an associative structure`keyBy($keyBy)`Re-keys by a field or callback`countBy($countBy)`Counts into an associative structure`mapWithKeys($callback)`The callback defines the keys`mapToDictionary($callback)`Produces a dictionary structure`mapToGroups($callback)`Produces a grouped structure`pluck($value, $key)`Only when `$key` is given -- it would re-key by that fieldIf you genuinely need one of these shapes, hand the data off to a regular `Collection`:

```
$grouped = collect($list->all())->groupBy('category');
```

All supported `Collection` methods
----------------------------------

[](#all-supported-collection-methods)

Everything else on `Illuminate\Support\Collection` works and keeps the invariant. A non-exhaustive list:

`add`, `all`, `chunk`, `collapse`, `concat`, `contains`, `count`, `diff`, `each`, `every`, `except`, `filter`, `first`, `firstWhere`, `flatMap`, `flatten`, `forget`, `get`, `implode`, `intersect`, `isEmpty`, `isNotEmpty`, `join`, `last`, `map`, `max`, `median`, `merge`, `min`, `nth`, `only`, `pad`, `partition`, `pipe`, `pluck` (without `$key`), `pop`, `prepend`, `pull`, `push`, `random`, `reduce`, `reject`, `replace`, `reverse`, `search`, `shift`, `shuffle`, `skip`, `skipUntil`, `skipWhile`, `slice`, `sole`, `some`, `sort`, `sortBy`, `sortByDesc`, `sortDesc`, `sortKeys`, `sortKeysDesc`, `splice`, `sum`, `take`, `takeUntil`, `takeWhile`, `toArray`, `toJson`, `transform`, `unique`, `unless`, `values`, `when`, `where`, `whereIn`, `whereNotIn`, `zip`.

Type safety
-----------

[](#type-safety)

`ListCollection` is annotated as `@extends Collection`, so PHPStan and your IDE understand that keys are always `int` and values keep their type through `map()`, `filter()`, `pluck()`, and friends. The package itself is analysed at PHPStan `level: max`.

Requirements
------------

[](#requirements)

- PHP 8.3+
- Laravel 11 or 12

Testing
-------

[](#testing)

```
composer test          # Pest test suite
composer analyse       # PHPStan at level max
composer format        # Pint
```

The test suite runs on PHP 8.3 and 8.4 against Laravel 11 and 12, on both Ubuntu and Windows.

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for recent changes.

Contributing
------------

[](#contributing)

Pull requests are welcome. Please run `composer test` and `composer analyse` before opening a PR, and add a Pest test for any new behaviour.

Credits
-------

[](#credits)

- [Daniel Heyne](https://github.com/derheyne)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see the [License File](LICENSE.md) for more information.

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance94

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

25d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/5128fff4560d34d46155b6bcefc1bf8680e24fbf2c75f61d712672790462f657?d=identicon)[dhy](/maintainers/dhy)

---

Top Contributors

[![derheyne](https://avatars.githubusercontent.com/u/6844603?v=4)](https://github.com/derheyne "derheyne (8 commits)")

---

Tags

collectionslaravellaravel-packagephplaravelDaniel Heynelaravel-list

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/derheyne-laravel-list/health.svg)

```
[![Health](https://phpackages.com/badges/derheyne-laravel-list/health.svg)](https://phpackages.com/packages/derheyne-laravel-list)
```

###  Alternatives

[spatie/laravel-data

Create unified resources and data transfer objects

1.8k33.0M871](/packages/spatie-laravel-data)[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k4.3M41](/packages/spatie-laravel-pdf)[codewithdennis/filament-select-tree

The multi-level select field enables you to make single selections from a predefined list of options that are organized into multiple levels or depths.

327482.0k25](/packages/codewithdennis-filament-select-tree)[nativephp/desktop

NativePHP for Desktop

37833.6k8](/packages/nativephp-desktop)[worksome/exchange

Check Exchange Rates for any currency in Laravel.

124581.3k](/packages/worksome-exchange)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3913.7k](/packages/rawilk-profile-filament-plugin)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
