benconda/collection

Collection library, powered by generators, with generics, easily extendable and immutable. Lazy by design and memory friendly. Accept anything as iterable 🔥 (Generator, array, Iterator, ...)

1.0.0 2024-07-19 15:52 UTC

This package is auto-updated.

Last update: 2024-11-29 08:32:06 UTC


README

Collection library, powered by generators, with generics, easily extendable and immutable. Lazy by design and memory friendly. Accept anything as iterable 🔥 (Generator, array, Iterator, ...)

Requirement

To use this library you need at least php 8.2

Usage

You first need to create the collection from an iterable, it can be an array, or anything that is iterable (generators, objects iterators, etc)

$collection = Collection::from($myIterable);

Now you can apply one or multiple Modifier to it :

use BenConda\Collection\Collection;
// [...]

$collection = Collection::from(range(1, 10))
    ->filter(callback: static fn(int $item): bool => $item > 5)
    ->map(on: fn(int $item): string => "The number is $item");

Each time you call a modifier method it returns a new instance of the collection, nothing is override.

The collection is invokable, so you can add modifiers like this too :

$collection = Collection::from(range(1, 10))
(
  new Filter(
    callback: fn(int $item): bool => $item >= 5
  )
)
(
  new Map(
    callback: fn(int $item): string => "The number is $item"
  )
);

Because each modifier rely on generators, nothing is done until you start to iterate on the collection.

You can get the first value like this :

$first = $collection->first(); // $first = The number is 5

Note that it won't go through the whole array to return the first value, behind the scene it only iterate through 1,2,3,4,5 => The number is 5 and stop.

This is possible thanks to generators, most other Collection libraries will need to loop through the whole array to filter it, then Map every filtered value before you can get the first value.

We can iterate on all value using a foreach directly on the collection $object :

$collection = Collection::from(range(1, 10))
foreach ($collection as $key => $item)
{
  // do what you want
}

And you can translate the whole Collection into an array like this :

$collection->toArray();

Be careful, by design we accept anything as a key, array restrict keys to int|string type, if the key type mismatch, it will fallback to int incremented key.

Modifiers

Collection iteration can be altered using modifiers, which allow you to shape and transform data in a memory friendly way.

Some modifiers require some memory buffering, and are split in another namespace called BufferedModifier. This is the case for example with BenConda\Collection\BufferedModifier\Reverse, in order to reverse, the whole collection need to be loaded in memory.

So keep in mind, BufferedModifier namespace = will consume memory depending on the size of the iterable.

You will find the modifiers list in this documentation

Note : documentation is in progress

Extend

Add custom modifier

Simply create a class that implement BenConda\Collection\Modifier\ModifierInterface

For example, for the reindex modifier :

use Generator;
use BenConda\Collection\Modifier\ModifierInterface;

/**
 * @template TKey
 * @template TValue
 *
 * @implements ModifierInterface<TKey, TValue>
 */
final class Reindex implements ModifierInterface
{
    /**
     *
     * @param iterable<TKey, TValue> $iterable
     *
     * @return Generator<int, TValue>
     */
    public function __invoke(iterable $iterable): Generator
    {
        foreach ($iterable as $value) {
            yield $value;
        }
    }
}

If you need some configuration, simply add a constructor to the class.

Using your own collection class

Sometimes you need to create your own, strict typed Collection class.

To do so, you can extend the CoreCollection class, and implement only the needed modifier method.

Sometimes you may need to have mutable custom Collection, for this use case you can extend MutableCoreCollection. You will find an example in this test

Note that even with mutable Collection, you still rely on Generators behind so each time you modify your collection, nothing is really done until you loop through it.