PHPackages                             harmenjanssen/lazycollection - 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. harmenjanssen/lazycollection

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

harmenjanssen/lazycollection
============================

Functional iterator patterns using generators.

332PHP

Since Sep 25Pushed 8y ago2 watchersCompare

[ Source](https://github.com/harmenjanssen/lazycollection)[ Packagist](https://packagist.org/packages/harmenjanssen/lazycollection)[ RSS](/packages/harmenjanssen-lazycollection/feed)WikiDiscussions master Synced 2d ago

READMEChangelogDependenciesVersions (1)Used By (0)

LazyCollection
==============

[](#lazycollection)

What's this?
------------

[](#whats-this)

A `LazyCollection` class that takes care of creating a [generator](http://php.net/manual/en/language.generators.php) for your collection.
Generators don't work with the standard `map`, `filter` and `reduce` functions in PHP.
This is a shame of course because you lose a lot of handy abstractions. This package implements those methods lazily.

Consider the following example with traditional arrays:

```
$square = function($x) {
  return $x * $x;
};
$isEven = function($x) {
  return $x % 2 === 0;
};

$highestEvenSquare = array_filter(
    array_map(
        $square,
        range(30, 0)
    ),
    $isEven
)[0];
```

This requires the entire range to be created, mapped to squares, and filtered before we can grab the first item.

With `LazyCollection`, this could be implemented like this:

```
$lazyCollection = new LazyCollection\Examples\LazyArray(range(29, 0));
$lazyCollection->map($square)
    ->filter($isEven)
    ->first();
```

Of course, the initial range will be created in memory, but only the first two items would need to be squared and filtered. Since the second item, 28, is a match to our filter function (28 \* 28 results in the even number 784), `first()` will just stop there. The rest of the squares won't be computed.

It's easy to imagine a collection where every next item is computed on the spot, in which case the original array doesn't even have to be created in memory (for instance: search a large database or file until a fixed number of matches is found).

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

[](#installation)

Install using Composer:

```
composer require harmenjanssen/lazycollection

```

Usage
-----

[](#usage)

`all()`

- This method returns the actual `Generator`:

```
$numbers = LazyCollection\Examples\Numbers::from(1);
foreach ($numbers->take(100)->all() as $n) {
    echo $n . "\n";
}
// will echo numbers 1 through 100
```

`first()`

- Take the first item of the collection.

`take(int $amount): Subset`

- Limits the collection to the given amount. Returns a new `LazyCollection` of type `Subset`. See example under `all()`.

`slice(int $start, int $length): Subset`

- Take a subset of `$length` items of the collection starting at `$start`.

```
$numbers = LazyCollection\Examples\Numbers::from(1);
$fifty = $numbers->slice(50, 10);

iterator_to_array($fifty); // [50, 51, ... 60]
```

`map(callable $transformer): Mapped`

- Add a mapping function to the collection. Returns a new `LazyCollection` of type `Mapped`.

```
$numbers = LazyCollection\Examples\Numbers::from(1);
$squares = $numbers->map(function($x) {
    return $x * $x;
});

iterator_to_array($squares->take(10)); // [1, 4, 9, 16, 25... 100]
```

`filter(callable $predicate): Filtered`

- Add a filter function to the collection. Returns a new `LazyCollection` of type `Filtered`.

```
$numbers = LazyCollection\Examples\Numbers::from(1);
$evenStevens = $numbers->filter(function($x) {
    return $x % 2 === 0;
});

iterator_to_array($evenStevens->take(10)); // [2, 4, 6, 8, 10... 20]
```

`reduce(callable $reducer, $seed)`

- Reduce the collection to a single value.

```
$numbers = LazyCollection\Examples\Numbers::from(1)->take(10);
$sum = $numbers->reduce(function($a, $b) {
    return $a + $b;
}, 0); // 55
```

Implement your own lazy collections
-----------------------------------

[](#implement-your-own-lazy-collections)

Create a subclass of `LazyCollection`, implementing the 4 required methods:

`start()`

- Returns the initial iteration. Note that this is not the same as an iteration's value, nor does it have to be numeric. You determine how you iterate your collection and what data you need to progress through it. See the Fibonacci example for a collection that uses an array to track progress through the collection

`next($iteration)`

- Returns the next iteration based on the previous iteration. Note that the return type and the type of its parameter are the same as those of `start()`, most likely.

`done(): bool`

- Wether iteration is done.

`value($iteration)`

- Return a value based on the given iteration.

Infinite collections
--------------------

[](#infinite-collections)

Infinite collection are elegant solutions to computational patterns. Both the [Numbers](https://github.com/harmenjanssen/lazycollection/blob/master/examples/Numbers.php) sequence and the [Fibonacci](https://github.com/harmenjanssen/lazycollection/blob/master/examples/Fibonacci.php) sequence are examples of infinite collections.
You can project a range of values, but there's no natural end.

Even though iterating over these collections is no problem, you have to make sure you bring iteration to a halt yourself, or you'll run out of memory. `take()` is a handy method to use in this case.

```
$fibonacci = new LazyCollection\Examples\Fibonacci;
iterator_to_array($fibonacci->take(10)->all()); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
```

Also consider this pitfall: when filtering an infinite collection by a predicate *that matches nothing*, the filter will not return an empty collection, as it would with regular arrays. It will run out of memory since it never ends.

```
// This will never resolve:

$fibonacci = new LazyCollection\Examples\Fibonacci;
$fibonacci->filter('is_string')->take(10);
```

Note also that you cannot use `iterator_to_array` to flatten the iterator into an array. Again, this would never end.

Todos
-----

[](#todos)

- Implement `until()`
- Implement `find()`

Inspiration
-----------

[](#inspiration)

I picked up the concept of functional iterators from the wonderful book [Javascript Allongé](https://leanpub.com/javascriptallongesix/), by [Reginald Braithwaite](https://github.com/raganwald).

Lots of interesting languages support the concept of lazy (possibly infinite) sequences, like Haskell and Clojure.

###  Health Score

22

—

LowBetter than 22% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity10

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity41

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.

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/803176?v=4)[Harmen Janssen](/maintainers/harmenjanssen)[@harmenjanssen](https://github.com/harmenjanssen)

---

Top Contributors

[![harmenjanssen](https://avatars.githubusercontent.com/u/803176?v=4)](https://github.com/harmenjanssen "harmenjanssen (7 commits)")

---

Tags

generatorslazyphp

### Embed Badge

![Health badge](/badges/harmenjanssen-lazycollection/health.svg)

```
[![Health](https://phpackages.com/badges/harmenjanssen-lazycollection/health.svg)](https://phpackages.com/packages/harmenjanssen-lazycollection)
```

PHPackages © 2026

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