API¶
On this page you’ll find a description of all the methods available in this library and their signature.
Static constructors¶
The following static methods acts as constructors.
empty¶
Create an empty Collection.
Signature: Collection::empty(): Collection;
$collection = Collection::empty();
fromCallable¶
Create a collection from a callable.
Tip
This can be very useful when working with a PHP Generator, since it will allow the collection object to behave as if the Generator was rewindable.
Signature: Collection::fromCallable(callable $callable, iterable $parameters = []): Collection;
$callback = static function (): Generator {
yield 'a';
yield 'b';
yield 'c';
};
$collection = Collection::fromCallable($callback);
fromFile¶
Create a collection from a file.
Signature: Collection::fromFile(string $filepath, ?Closure $consumer = null): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
// Get data from a file
Collection::fromFile('/etc/passwd');
// Get data from a URL
Collection::fromFile('http://loripsum.net/api');
// Get data from a CSV file
Collection::fromFile('data.csv', fgetcsv(...));
fromGenerator¶
Create a collection from a Generator.
Warning
The difference between this constructor and fromIterable
is that
the generator is decorated with a caching Iterator. Generators
are not
rewindable by design and using fromGenerator
automatically adds the
caching layer for you.
Tip
You can reproduce the same behaviour by using fromIterable
directly
followed by the cache
operation.
Signature: Collection::fromGenerator(Generator $generator): Collection;
$generator = (static fn () => yield from range(1, 5))();
$generator->next();
$generator->next();
$collection = Collection::fromGenerator($generator)
->all(); // [0 => 3, 1 => 4, 2 => 5]
fromIterable¶
Create a collection from an iterable.
Warning
When instantiating from a PHP Generator, the collection object will inherit its behaviour:
it will only be iterable a single time, and an exception will be thrown if multiple operations which attempt
to re-iterate are applied, for example count()
. To circumvent this internal PHP limitation, use
Collection::fromGenerator()
or better Collection::fromCallable()
which requires the generating
callable not yet initialized.
Signature: Collection::fromIterable(iterable $iterable): Collection;
$collection = Collection::fromIterable(['a', 'b', 'c']);
fromResource¶
Create a collection from a resource.
Signature: Collection::fromResource($resource): Collection;
$stream = fopen('data://text/plain,' . $string, 'rb');
$collection = Collection::fromResource($stream);
fromString¶
Create a collection from a string.
Signature: Collection::fromString(string $string, string $delimiter = ''): Collection;
$data = file_get_contents('http://loripsum.net/api');
$collection = Collection::fromString($data);
range¶
Build a collection from a range of values.
Signature: Collection::range(float $start = 0.0, float $end = INF, float $step = 1.0): Collection;
$even = Collection::range(0, 20, 2); // [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
times¶
Create a collection by invoking a callback a given amount of times.
If no callback is provided, then it will create a simple list of incremented integers.
Signature: Collection::times(int $number = 0, ?callable $callback = null): Collection;
$collection = Collection::times(5); // [1, 2, 3, 4, 5]
unfold¶
Create a collection by yielding from a callback with an initial value.
Warning
The callback needs to return a list of values which will be reused as callback arguments on the next callback call. Therefore, the returned list should contain values of the same type as the parameters for the callback function.
Signature: Collection::unfold(callable $callback, array $parameters = []): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
use const INF;
include __DIR__ . '/../../../../vendor/autoload.php';
// Example 1 -> A list of Naturals from 1 to Infinity (use `limit` to keep only a set of them)
Collection::unfold(static fn (int $n): array => [$n + 1], [1])
->unwrap()
->all(); // [1, 2, 3, 4, ...]
// Example 2 -> fibonacci sequence
Collection::unfold(static fn (int $a = 0, int $b = 1): array => [$b, $a + $b])
->pluck(0)
->limit(10)
->all(); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
// Example 3 -> infinite range, similar to the `range` operation
$even = Collection::unfold(static fn ($carry): array => [$carry + 2], [-2])->unwrap();
$odd = Collection::unfold(static fn ($carry): array => [$carry + 2], [-1])->unwrap();
// Is the same as
$even = Collection::range(0, INF, 2);
$odd = Collection::range(1, INF, 2);
Methods (operations)¶
Operations are pure functions which can be used to manipulate an iterator, either directly
or through the Collection
object.
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
use loophp\collection\Operation\Filter;
include __DIR__ . '/../../../../vendor/autoload.php';
$input = [1, 2, 3, 4];
$even = static fn (int $value): bool => $value % 2 === 0;
// Standalone usage
$filtered = Filter::of()($even)($input);
print_r(iterator_to_array($filtered)); // [2, 4]
// Usage via Collection object
$filtered = Collection::fromIterable($input)->filter($even);
print_r($filtered->all()); // [2, 4]
When used separately, operations typically return a PHP Generator or an Iterator.
When used as a Collection
method, operations fall into a few main categories based on the return type:
Operations that return a
boolean
orscalar
value:Contains
,Count
,Equals
,Every
,Falsy
,Has
,IsEmpty
,Match
(orMatchOne
),Nullsy
,Truthy
.Operations that return a
Collection
ofCollection
objects:Partition
,Span
.Operations that return keys/values from the collection:
All
,Current
,Key
.Operations that return a new
Collection
object: all other operations.
Note
The Key
operation can return any value because Collection
leverages PHP Generators,
which allow using any type as a key as opposed to array
, which only allows int|string
keys.
Note
Earlier versions of the package had most operations returning a new Collection
object.
This was changed based on convenience and ease of use; typical usage of operations which return boolean
values
would involve immediately retrieving the value inside, whereas for most other operations further transformations
are likely to be applied.
all¶
Convert the collection into an array, re-indexing keys using Collection::normalize()
to prevent data loss by default.
Warning
Before version 6.0.0, this operation did not re-index keys by default, which meant at times data loss could occur.
The reason data loss could occur is because PHP array keys cannot be duplicated and must either be int
or string
.
The old behaviour can still be achieved by using the operation with the $normalize
parameter as false.
Interface: Allable
Signature: Collection::all(bool $normalize = true): array;
<?php
declare(strict_types=1);
namespace App;
use Generator;
use loophp\collection\Collection;
use loophp\collection\Operation\All;
use loophp\collection\Operation\Filter;
use loophp\collection\Operation\Pipe;
include __DIR__ . '/../../../../vendor/autoload.php';
// Example 1 -> usage with "list" collection
$generator = static function (): Generator {
yield 0 => 'a';
yield 1 => 'b';
yield 0 => 'c';
yield 2 => 'd';
};
$collection = Collection::fromIterable($generator());
print_r($collection->all(false)); // [0 => 'c', 1 => 'b', 2 => 'd']
$collection = Collection::fromIterable($generator());
print_r($collection->all()); // ['a', 'b', 'c', 'd']
// Example 2 -> usage with "map" collection
$collection = Collection::fromIterable(['foo' => 1, 'bar' => 2]);
print_r($collection->all(false)); // ['foo' => 1, 'bar' => 2]
print_r($collection->all()); // [1, 2]
// Example 3 -> standalone operation usage
$even = static fn (int $value): bool => $value % 2 === 0;
$piped = Pipe::of()(Filter::of()($even), All::of()(true))([1, 2, 3, 4]);
print_r(iterator_to_array($piped)); // [2, 4]
append¶
Add one or more items to a collection.
Warning
This operation maintains the keys of the appended items. If you wish to re-index the keys you can use the
Collection::normalize()
operation, or Collection::all()
when converting into an array, which will apply
normalize
by default.
Interface: Appendable
Signature: Collection::append(mixed ...$items): Collection;
Collection::fromIterable([1 => '1', 2 => '2', 3 => '3'])
->append('4'); // [1 => '1', 2 => '2', 3 => '3', 0 => '4']
Collection::fromIterable(['1', '2', '3'])
->append('4')
->append('5', '6')
->all(); // ['1', '2', '3', '4', '5', '6']
apply¶
Execute callback(s) on each element of the collection.
Iterates on the collection items regardless of the return value of the callback.
If the callback does not return true
then it stops applying callbacks on subsequent items.
Interface: Applyable
Signature: Collection::apply(callable ...$callbacks): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
$callback = static function ($value, $key): bool {
var_dump('Value is: ' . $value . ', key is: ' . $key);
return true;
};
$collection = Collection::fromIterable(['1', '2', '3']);
$collection
->apply($callback)
->squash(); // trigger the callback
/* Will print:
string(22) "Value is: 1, key is: 0"
string(22) "Value is: 2, key is: 1"
string(22) "Value is: 3, key is: 2"
*/
associate¶
Transform keys and values of the collection independently and combine them.
Interface: Associateable
Signature: Collection::associate(?callable $callbackForKeys = null, ?callable $callbackForValues = null): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
// Example 1: Both callbacks are provided
$input = range(1, 5);
Collection::fromIterable($input)
->associate(
static fn ($key, $value) => $key * 2,
static fn ($value, $key) => $value * 3
);
// [
// 0 => 3,
// 2 => 6,
// 4 => 9,
// 6 => 12,
// 8 => 15,
// ]
// Example 2: Only the callback for keys is provided
$input = range(1, 5);
Collection::fromIterable($input)
->associate(
static fn ($key, $value) => $key * 2
);
// [
// 0 => 1,
// 2 => 2,
// 4 => 3,
// 6 => 4,
// 8 => 5,
// ]
// Example 3: Only the callback for values is provided
$input = range(1, 5);
Collection::fromIterable($input)
->associate(
null,
static fn ($value, $key) => $value * 3
);
// [
// 0 => 3,
// 1 => 6,
// 2 => 9,
// 3 => 12,
// 4 => 15,
// ]
asyncMap¶
Asynchronously apply a single callback to every item of a collection and use the return value.
Warning
This method requires amphp/parallel-functions to be installed.
Warning
This operation is non-deterministic, we cannot ensure the order of the elements at the end. Additionally,
keys are preserved - use the Collection::normalize
operation if you want to re-index the keys.
Warning
An earlier version of this operation allowed usage with multiple callbacks. This behaviour
was removed in version 5.0
; asyncMapN
should be used instead, or,
alternatively, multiple successive asyncMap
calls can achieve the same result.
Interface: AsyncMapable
Signature: Collection::asyncMap(callable callback): Collection;
$mapper = static function(int $value): int {
sleep($value);
return $value * 2;
};
$collection = Collection::fromIterable(['c' => 3, 'b' => 2, 'a' => 1])
->asyncMap($mapper); // ['a' => 2, 'b' => 4, 'c' => 6]
asyncMapN¶
Asynchronously apply one or more supplied callbacks to every item of a collection and use the return value.
Tip
This operation is best used when multiple callbacks need to be applied. If you only want to apply
a single callback, asyncMap
should be preferred as it benefits from more specific type hints.
Warning
This method requires amphp/parallel-functions to be installed.
Warning
This operation is non-deterministic, we cannot ensure the order of the elements at the end. Additionally,
keys are preserved - use the Collection::normalize
operation if you want to re-index the keys.
Interface: AsyncMapNable
Signature: Collection::asyncMapN(callable ...$callbacks): Collection;
$mapper1 = static function(int $value): int {
sleep($value);
return $value;
};
$mapper2 = static function(int $value): int {
return $value * 2;
};
$collection = Collection::fromIterable(['c' => 3, 'b' => 2, 'a' => 1])
->asyncMapN($mapper1, $mapper2); // ['a' => 2, 'b' => 4, 'c' => 6]
averages¶
Calculate the average of a collection of numbers.
The average constitute the result obtained by adding together several amounts and then dividing this total by the number of amounts.
Based on scanLeft1, this operation will return the average at each iteration. Therefore, if you’re looking for one single result, you must get the last item using last operation.
Interface: Averagesable
Signature: Collection::averages(): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
$collection = Collection::fromIterable([1, 2, 3, 4, 5])
->averages(); // [1, 1.5, 2, 2.5, 3] from [1/1, (1+2)/2, (1+2+3)/3 ...]
$average = Collection::fromIterable([1, 2, 3, 4, 5])
->averages()
->last(); // [3]
$collection = Collection::empty()
->averages(); // []
cache¶
Useful when using a resource as input and you need to run through the collection multiple times.
Interface: Cacheable
Signature: Collection::cache(?CacheItemPoolInterface $cache = null): Collection;
$fopen = fopen(__DIR__ . '/vendor/autoload.php', 'r');
$collection = Collection::fromResource($fopen)
->cache();
chunk¶
Chunk a collection of items into chunks of items of a given size.
Interface: Chunkable
Signature: Collection::chunk(int ...$sizes): Collection;
$collection = Collection::fromIterable(range(0, 6))
->chunk(2); // [[0, 1], [2, 3], [4, 5], [6]]
$collection = Collection::fromIterable(range(0, 6))
->chunk(1, 2); // [[0], [1, 2], [3], [4, 5], [6]]
coalesce¶
Return the first non-nullsy value in a collection.
Nullsy values are:
The null value:
null
Empty array:
[]
The integer zero:
0
The boolean:
false
The empty string:
''
Interface: Coalesceable
Signature: Collection::coalesce(): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
$input = range('a', 'e');
$collection = Collection::fromIterable($input)
->coalesce(); // [ 0 => 'a' ]
$input = ['', null, 'foo', false, ...range('a', 'e')];
$collection = Collection::fromIterable($input)
->coalesce(); // [ 2 => 'foo' ]
collapse¶
Collapse a collection of items into a simple flat collection.
Warning
Key differences compared to flatten
are that only one level will be collapsed and values at the bottom level will be removed.
Interface: Collapseable
Signature: Collection::collapse(): Collection;
$collection = Collection::fromIterable([[1], [2, 3], [4, [5]]])
->collapse(); // [1, 2, 3, 4, [5]]
$collection = Collection::fromIterable([1, 2, [3]])
->collapse(); // [3]
column¶
Return the values from a single column in the input iterables.
Tip
If the iterables you are selecting from are Generators
, the operation will allow
accessing keys of any type, not just int|string
.
Interface: Columnable
Signature: Collection::column(mixed $column): Collection;
$records = [
[
'id' => 2135,
'first_name' => 'John',
'last_name' => 'Doe',
],
[
'id' => 3245,
'first_name' => 'Sally',
'last_name' => 'Smith',
],
[
'id' => 5342,
'first_name' => 'Jane',
'last_name' => 'Jones',
],
[
'id' => 5623,
'first_name' => 'Peter',
'last_name' => 'Doe',
],
];
$result = Collection::fromIterable($records)
->column('first_name'); // ['John', 'Sally', 'Jane', 'Peter']
$result = Collection::fromIterable($records)
->column('non_existent_key'); // []
combinate¶
Get all the combinations of a given length of a collection of items.
Interface: Combinateable
Signature: Collection::combinate(?int $length = null): Collection;
$collection = Collection::fromIterable(['a', 'b', 'c'])
->combinate(2); // [['a', 'b'], ['b', 'c'], ['a', 'c']]
combine¶
Combine a collection of items with some other keys.
Note
When the two sets to combine are not equal in size, null is used as filling value.
Interface: Combineable
Signature: Collection::combine(mixed ...$keys): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
$result = Collection::fromIterable(range('A', 'E'))
->combine(...range('e', 'a')); // ['e' => 'A', 'd' => 'B', 'c' => 'C', 'b' => 'D', 'a' => 'E']
$result = Collection::fromIterable(range('a', 'e'))
->combine(...range('a', 'c')); // ['a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => null, 'e' => null]
$result = Collection::fromIterable(range('a', 'c'))
->combine(...range('a', 'e')); // ['a' => 'a', 'b' => 'b', 'c' => 'c', null => 'd', null => 'e']
compact¶
Remove given values from the collection; if no values are provided, it removes nullsy values.
Nullsy values are:
The null value:
null
Empty array:
[]
The integer zero:
0
The boolean:
false
The empty string:
''
Interface: Compactable
Signature: Collection::compact(mixed ...$values): Collection;
$collection = Collection::fromIterable(['a', 1 => 'b', null, false, 0, 'c'])
->compact(); // [0 => 'a', 1 => 'b', 5 => 'c']
$collection = Collection::fromIterable(['a', 1 => 'b', null, false, 0, 'c'])
->compact(null, 0); // [0 => 'a', 1 => 'b', 3 => false, 5 => 'c']
compare¶
Fold the collection through a comparison operation, yielding the “highest” or “lowest” element as defined by the comparator callback. The callback takes a pair of two elements and should return the “highest” or “lowest” one as desired.
If no custom logic is required for the comparison, the simpler max
or min
operations
can be used instead.
Tip
This operation is a specialised application of foldLeft1
.
Interface: Comparable
Signature: Collection::compare(callable $comparator, mixed $default = null): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
use stdClass;
use const INF;
include __DIR__ . '/../../../../vendor/autoload.php';
$callback = static fn (stdClass $left, stdClass $right): stdClass => $left->age > $right->age
? $left
: $right;
$input = [
(object) ['id' => 2, 'age' => 5],
(object) ['id' => 1, 'age' => 10],
];
$result = Collection::fromIterable($input)
->compare($callback); // (object) ['id' => 1, 'age' => 10]
$result = Collection::empty()
->compare($callback, -INF); // -INF
contains¶
Check if the collection contains one or more values.
Warning
The values
parameter is variadic and will be evaluated as a logical OR
.
Interface: Containsable
Signature: Collection::contains(mixed ...$values): bool;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
// Does it contains the letter 'd' ?
$result = Collection::fromIterable(range('a', 'c'))
->contains('d'); // false
// Does it contains the letter 'a' or 'z' ?
$result = Collection::fromIterable(range('a', 'c'))
->contains('a', 'z'); // true
// Does it contains the letter 'd' ?
$result = Collection::fromIterable(['a' => 'b', 'c' => 'd'])
->contains('d'); // true
count¶
Returns the number of elements in a collection.
Tip
If you only want to check whether the collection is empty or not, use the isEmpty
operation as it will be significant more performant in a large collection.
Interface: Countable
Signature: Collection::count(): int;
$collection = Collection::fromIterable(range('a', 'c'))
->count(); // 3
countIn¶
This operation requires a reference to a parameter that will contain the amount of items in the collection.
The difference with the count operation is that the count operation will return the amount of items in the collection and the countIn operation will yield over the collection itself while updating the counter variable.
Interface: CountInAble
Signature: Collection::countIn(int &$counter): Collection;
<?php
declare(strict_types=1);
include __DIR__ . '/../../../../vendor/autoload.php';
use loophp\collection\Collection;
$lettersCounter = $wordsCounter = 0;
$collection = Collection::fromString('The quick brown fox jumps over the lazy dog')
->countIn($lettersCounter)
->words()
->countIn($wordsCounter)
->map(
static function (string $word, int $k) use ($wordsCounter): string {
return sprintf('[%s/%s]: %s', $k + 1, $wordsCounter, $word);
}
)
->all(); // [ "[1/9]: The", "[2/9]: quick", "[3/9]: brown", "[4/9]: fox", "[5/9]: jumps", "[6/9]: over", "[7/9]: the", "[8/9]: lazy", "[9/9]: dog" ]
print_r($wordsCounter); // 9
print_r($lettersCounter); // 43
current¶
Get the value of an item in the collection given a numeric index or the default 0
.
If the given numeric index is out of bound, current will return a default value null
that can be modified by providing a second argument to the operation.
Interface: Currentable
Signature: Collection::current(int $index = 0, mixed $default = null);
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
Collection::fromIterable(['a', 'b', 'c', 'd'])->current(); // Return 'a'
Collection::fromIterable(['a', 'b', 'c', 'd'])->current(0); // Return 'a'
Collection::fromIterable(['a', 'b', 'c', 'd'])->current(1); // Return 'b'
Collection::fromIterable(['a', 'b', 'c', 'd'])->current(10); // Return null
Collection::fromIterable(['a', 'b', 'c', 'd'])->current(10, 'unavailable'); // Return 'unavailable'
cycle¶
Cycle indefinitely around a collection of items.
Interface: Cycleable
Signature: Collection::cycle(): Collection;
$collection = Collection::fromIterable(['a', 'b', 'c', 'd'])
->cycle();
diff¶
Compares the collection against another collection, iterable, or set of multiple values. This method will return the values in the original collection that are not present in the given argument set.
Interface: Diffable
Signature: Collection::diff(mixed ...$values): Collection;
$collection = Collection::fromIterable(['a', 'b', 'c', 'd', 'e'])
->diff('a', 'b', 'c', 'x'); // [3 => 'd', 4 => 'e']
diffKeys¶
Compares the collection against another collection, iterable, or set of multiple keys. This method will return the key / value pairs in the original collection that are not present in the given argument set.
Interface: Diffkeysable
Signature: Collection::diffKeys(mixed ...$keys): Collection;
$collection = Collection::fromIterable(['a', 'b', 'c', 'd', 'e'])
->diffKeys(1, 2); // [0 => 'a', 3 => 'd', 4 => 'e']
dispersion¶
This method extends the library’s functionality by allowing users to calculate a dispersion value using the amount of value changes within the collection.
For example, the set [a, b, a] contains three elements and two changes. From a to b and from b to a. Therefore, the dispersion value is 2 / 2 = 1.
The dispersion value is normalized between 0 and 1. A dispersion of 0 indicates no dispersion, while a dispersion of 1 means maximum dispersion.
Interface: Dispersionable
Signature: Collection::dispersion(): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
$collection = Collection::fromIterable(['a'])
->dispersion(); // [0]
$collection = Collection::fromIterable(['a', 'b'])
->dispersion(); // [0, 1]
$collection = Collection::fromIterable(['a', 'b', 'a'])
->dispersion(); // [0, 1, 1]
$collection = Collection::fromIterable(['a', 'b', 'b'])
->dispersion(); // [0, 1, .5]
distinct¶
Remove duplicated values from a collection, preserving keys.
The operation has 2 optional parameters that allow you to customize precisely how values are accessed and compared to each other.
The first parameter is the comparator. This is a curried function which takes first the left part, then the right part and then returns a boolean.
The second parameter is the accessor. This binary function takes the value and the key of the current iterated value and then return the value to compare. This is useful when you want to compare objects.
Interface: Distinctable
Signature: Collection::distinct(?callable $comparatorCallback = null, ?callable $accessorCallback = null): Collection;
<?php
declare(strict_types=1);
namespace App;
use Closure;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
// Example 1 -> Using the default callbacks, with scalar values
$collection = Collection::fromIterable(['a', 'b', 'a', 'c'])
->distinct(); // [0 => 'a', 1 => 'b', 3 => 'c']
// Example 2 -> Using a custom comparator callback, with object values
final class User
{
public function __construct(private string $name) {}
public function name(): string
{
return $this->name;
}
}
$users = [
new User('foo'),
new User('bar'),
new User('foo'),
new User('a'),
];
$collection = Collection::fromIterable($users)
->distinct(
static fn (User $left): Closure => static fn (User $right): bool => $left->name() === $right->name()
); // [0 => User<foo>, 1 => User<bar>, 3 => User<a>]
// Example 3 -> Using a custom accessor callback, with object values
final class Person
{
public function __construct(private string $name) {}
public function name(): string
{
return $this->name;
}
}
$users = [
new Person('foo'),
new Person('bar'),
new Person('foo'),
new Person('a'),
];
$collection = Collection::fromIterable($users)
->distinct(
null,
static fn (Person $person): string => $person->name()
); // [0 => Person<foo>, 1 => Person<bar>, 3 => Person<a>]
// Example 4 -> Using both accessor and comparator callbacks, with object values
final class Cat
{
public function __construct(private string $name) {}
public function name(): string
{
return $this->name;
}
}
$users = [
new Cat('izumi'),
new Cat('nakano'),
new Cat('booba'),
new Cat('booba'),
];
$collection = Collection::fromIterable($users)
->distinct(
static fn (string $left): Closure => static fn (string $right): bool => $left === $right,
static fn (Cat $cat): string => $cat->name()
); // [0 => Cat<izumi>, 1 => Cat<nakano>, 2 => Cat<booba>]
drop¶
Drop the first n
items of the collection.
Interface: Dropable
Signature: Collection::drop(int $count): Collection;
Collection::fromIterable(range(10, 20))
->drop(2); // [12,13,14,15,16,17,18,19,20]
dropWhile¶
Iterate over the collection items and takes from it its elements from the moment when the condition fails for the first time till the end of the list.
Warning
The callbacks parameter is variadic and will be evaluated as a logical OR
.
If you’re looking for a logical AND
, you have to make multiple calls to the
same operation.
Interface: DropWhileable
Signature: Collection::dropWhile(callable ...$callbacks): Collection;
$isSmallerThanThree = static function (int $value): bool {
return 3 > $value;
};
Collection::fromIterable([1,2,3,4,5,6,7,8,9,1,2,3])
->dropWhile($isSmallerThanThree); // [3,4,5,6,7,8,9,1,2,3]
dump¶
Dump one or multiple items. It uses symfony/var-dumper if it is available,
var_dump() otherwise. A custom callback
can be also used.
Interface: Dumpable
Signature: Collection::dump(string $name = '', int $size = 1, ?Closure $closure = null): Collection;
Collection::fromIterable(range('a', 'e'))
->dump('Debug', 2); // Will debug the 2 first values.
duplicate¶
Find duplicated values from the collection.
The operation has 2 optional parameters that allow you to customize precisely how values are accessed and compared to each other.
The first parameter is the comparator. This is a curried function which takes first the left part, then the right part and then returns a boolean.
The second parameter is the accessor. This binary function takes the value and the key of the current iterated value and then return the value to compare. This is useful when you want to compare objects.
Interface: Duplicateable
Signature: Collection::duplicate(?callable $comparatorCallback = null, ?callable $accessorCallback = null): Collection;
<?php
declare(strict_types=1);
namespace App;
use Closure;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
// Example 1 -> Using the default callbacks, with scalar values
$collection = Collection::fromIterable(['a', 'b', 'a', 'c', 'a', 'c'])
->duplicate(); // [2 => 'a', 4 => 'a', 5 => 'c']
// Example 2 -> Using a custom comparator callback, with object values
final class User
{
public function __construct(private string $name) {}
public function name(): string
{
return $this->name;
}
}
$users = [
new User('foo'),
new User('bar'),
new User('foo'),
new User('a'),
];
$collection = Collection::fromIterable($users)
->duplicate(
static fn (User $left): Closure => static fn (User $right): bool => $left->name() === $right->name()
); // [2 => User<foo>]
// Example 3 -> Using a custom accessor callback, with object values
final class Person
{
public function __construct(private string $name) {}
public function name(): string
{
return $this->name;
}
}
$people = [
new Person('foo'),
new Person('bar'),
new Person('foo'),
new Person('a'),
];
$collection = Collection::fromIterable($people)
->duplicate(
null,
static fn (Person $person): string => $person->name()
); // [2 => Person<foo>]
// Example 4 -> Using both accessor and comparator callbacks, with object values
final class Cat
{
public function __construct(private string $name) {}
public function name(): string
{
return $this->name;
}
}
$cats = [
new Cat('izumi'),
new Cat('nakano'),
new Cat('booba'),
new Cat('booba'),
];
$collection = Collection::fromIterable($cats)
->duplicate(
static fn (string $left): Closure => static fn (string $right): bool => $left === $right,
static fn (Cat $cat): string => $cat->name()
); // [3 => Cat<booba>]
entropy¶
This method extends the library’s functionality by allowing users to calculate the normalized Shannon entropy of each item in a collection. Since it is not possible to calculate it lazily just like in the average operation, this operation returns a collection of entropy values, calculated individually for each item. Therefore, if you’re looking for the entropy of the whole collection, you must get the last item using last operation.
The implementation provides the normalized version of Shannon entropy. Normalization ensures that the entropy value is scaled between 0 and 1, making it easier to compare entropy across different collections, irrespective of their size or the diversity of elements they contain.
An entropy of 0 indicates no diversity (all elements are the same), while an entropy of 1 signifies maximum diversity (all elements are different).
Interface: Entropyable
Signature: Collection::entropy(): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
$collection = Collection::fromIterable(['a'])
->entropy(); // [0]
$collection = Collection::fromIterable(['a', 'b'])
->entropy(); // [0, 1]
$collection = Collection::fromIterable(['a', 'b', 'a'])
->entropy(); // [0, 1, 0.9182958340544896]
equals¶
Compare two collections for equality. Collections are considered equal if:
they have the same number of elements;
they contain the same elements, regardless of the order they appear in or their keys.
Elements will be compared using strict equality (===
). If you want to customize how elements
are compared or the order in which the keys/values appear is important, use the same
operation.
Tip
This operation enables comparing Collection
objects in PHPUnit tests using
the dedicated assertObjectEquals assertion.
Warning
Because this operation needs to traverse both collections to determine if
the same elements are contained within them, a performance cost is incurred. The operation will stop
as soon as it encounters an element of one collection that cannot be found in the other. However,
it is not recommended to use it for potentially large collections, where same
can be used instead.
Interface: Equalsable
Signature: Collection::equals(Collection $other): bool;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
Collection::fromIterable([1, 2, 3])
->equals(Collection::fromIterable([1, 2, 3])); // true
Collection::fromIterable([1, 2, 3])
->equals(Collection::fromIterable([3, 1, 2])); // true
Collection::fromIterable([1, 2, 3])
->equals(Collection::fromIterable([1, 2])); // false
Collection::fromIterable([1, 2, 3])
->equals(Collection::fromIterable([1, 2, 4])); // false
Collection::fromIterable(['foo' => 'f'])
->equals(Collection::fromIterable(['foo' => 'f'])); // true
Collection::fromIterable(['foo' => 'f'])
->same(Collection::fromIterable(['bar' => 'f'])); // true
Collection::fromIterable(['foo' => 'f', 'bar' => 'b'])
->equals(Collection::fromIterable(['foo' => 'f', 'baz' => 'b'])); // true
$a = (object) ['id' => 'a'];
$a2 = (object) ['id' => 'a'];
Collection::fromIterable([$a])
->equals(Collection::fromIterable([$a])); // true
Collection::fromIterable([$a])
->equals(Collection::fromIterable([$a2])); // false
every¶
Check whether all elements in the collection pass the test implemented by the provided callback(s).
Warning
The callbacks
parameter is variadic and will be evaluated as a logical OR
.
Interface: Everyable
Signature: Collection::every(callable ...$callbacks): bool;
$callback = static function (int $value): bool => $value < 20;
$result = Collection::fromIterable(range(0, 10))
->every($callback); // true
$result = Collection::fromIterable(range(0, 10))
->append(21)
->every($callback); // false
$result = Collection::fromIterable([])
->every($callback); // true
explode¶
Explode a collection into subsets based on a given value.
This operation uses the split
operation with the flag Splitable::REMOVE
and thus, values used to explode the
collection are removed from the chunks.
Interface: Explodeable
Signature: Collection::explode(mixed ...$explodes): Collection;
$collection = Collection::fromString('I am a text.')
->explode(' '); // [['I', 'a', 'm', 'a', 't', 'e', 'x', 't', '.']]
falsy¶
Check if the collection contains only falsy values. A value is determined to be falsy by applying a bool
cast.
Interface: Falsyable
Signature: Collection::falsy(): bool;
$result = Collection::fromIterable([2, 3, 4])
->falsy(); // false
$result = Collection::fromIterable([2, null, 4])
->falsy(); // false
$result = Collection::fromIterable(['', null, 0])
->falsy(); // true
filter¶
Filter collection items based on one or more callbacks.
Warning
The callbacks parameter is variadic and will be evaluated as a logical OR
.
If you’re looking for a logical AND
, you have to make multiple calls to the
same operation.
Tip
It is only when the callback returns true
that the value is kept.
Tip
If you’re looking for keeping the value in the iterator when the return is false
, see the reject
operation.
Interface: Filterable
Signature: Collection::filter(callable ...$callbacks): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
$divisibleBy2 = static fn ($value): bool => 0 === $value % 2;
$divisibleBy3 = static fn ($value): bool => 0 === $value % 3;
// Filter values divisible by 3.
$collection = Collection::fromIterable(range(1, 10))
->filter($divisibleBy3); // [3, 6, 9]
// Filter values divisible by 2 or 3.
$collection = Collection::fromIterable(range(1, 10))
->filter($divisibleBy2, $divisibleBy3); // [2, 3, 4, 6, 8, 9, 10]
// Filter values divisible by 2 and 3.
$collection = Collection::fromIterable(range(1, 10))
->filter($divisibleBy2)
->filter($divisibleBy3); // [6]
find¶
Find a collection item using one or more callbacks. If the value cannot be found, that is, no callback returns true for
any collection item, it will return the $default
value.
Warning
The callbacks
parameter is variadic and will be evaluated as a logical OR
.
If you’re looking for a logical AND
, you have to make multiple calls to the
same operation.
Tip
This operation is a shortcut for filter
+ current
.
Tip
It is only when the callback returns true
that the value is selected.
Interface: Findable
Signature: Collection::find(mixed $default = null, callable ...$callbacks);
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
use function range;
include __DIR__ . '/../../../../vendor/autoload.php';
$divisibleBy3 = static fn ($value): bool => 0 === $value % 3;
// Example 1: find a value and use the default `null` if not found
$value = Collection::fromIterable(range(1, 10))
->find(null, $divisibleBy3); // 3
$value = Collection::fromIterable([1, 2, 4])
->find(null, $divisibleBy3); // null
$value = Collection::fromIterable(['foo' => 'f', 'bar' => 'b'])
->find(null, static fn ($value): bool => 'b' === $value); // 'b'
$value = Collection::fromIterable(['foo' => 'f', 'bar' => 'b'])
->find(null, static fn ($value): bool => 'x' === $value); // null
// Example 2: find a value and use a custom default if not found
$value = Collection::fromIterable([1, 2, 4])
->find(-1, $divisibleBy3); // -1
$value = Collection::fromIterable(['foo' => 'f', 'bar' => 'b'])
->find(404, static fn ($value): bool => 'x' === $value); // 404
// Example 3: use with a Doctrine Query
/** @var EntityManagerInterface $em */
$q = $em->createQuery('SELECT u FROM MyProject\Model\Product p');
$isBook = static fn ($product): bool => 'books' === $product->getCategory();
$value = Collection::fromIterable($q->toIterable())
->find(null, $isBook);
first¶
Get the first item from the collection in a separate collection. Alias for head
.
Interface: Firstable
Signature: Collection::first(mixed $default = null): mixed;
$generator = static function (): Generator {
yield 'a' => 'a';
yield 'b' => 'b';
yield 'c' => 'c';
};
Collection::fromIterable($generator())
->first(); // a
flatMap¶
Transform the collection using a callback and keep the return value, then flatten it one level.
The supplied callback needs to return an iterable
: either an array
or a class that implements Traversable.
Tip
This operation is nothing more than a shortcut for map
+ flatten(1)
, or map
+ unwrap
.
Warning
Keys are preserved, use the Collection::normalize
operation if you want to re-index the keys.
Interface: FlatMapable
Signature: Collection::flatMap(callable $callback): Collection;
$square = static fn (int $val): int => $val ** 2;
$squareArray = static fn (int $val): array => [$val ** 2];
$squareCollection = static fn (int $val): Collection => Collection::fromIterable([$val ** 2]);
$collection = Collection::fromIterable(range(1, 3))
->flatMap($squareArray); // [1, 4, 9]
$collection = Collection::fromIterable(range(1, 3))
->flatMap($squareCollection); // [1, 4, 9]
$collection = Collection::fromIterable(range(1, 3))
->map($square)
->flatten(1); // [1, 4, 9]
$collection = Collection::fromIterable(range(1, 3))
->map($square)
->unwrap(); // [1, 4, 9]
flatten¶
Flatten a collection of items into a simple flat collection.
Interface: Flattenable
Signature: Collection::flatten(int $depth = PHP_INT_MAX): Collection;
$collection = Collection::fromIterable([0, [1, 2], [3, [4, [5, 6]]]])
->flatten(); // [0, 1, 2, 3, 4, 5, 6]
$collection = Collection::fromIterable([0, [1, 2], [3, [4, 5]])
->flatten(1); // [0, 1, 2, 3, [4, 5]]
flip¶
Flip keys and items in a collection.
Interface: Flipable
Signature: Collection::flip(): Collection;
$collection = Collection::fromIterable(['a', 'b', 'c', 'a'])
->flip();
Tip
array_flip() and Collection::flip() can behave differently, check the following examples.
When using regular arrays, array_flip() can be used to remove duplicates (deduplicate an array).
$dedupArray = array_flip(array_flip(['a', 'b', 'c', 'd', 'a']));
This example will return ['a', 'b', 'c', 'd']
.
However, when using a collection:
$dedupCollection = Collection::fromIterable(['a', 'b', 'c', 'd', 'a'])
->flip()
->flip()
->all();
This example will return ['a', 'b', 'c', 'd', 'a']
.
foldLeft¶
Takes the initial value and the first item of the collection and applies the
function to them, then feeds the function with this result and the second
argument and so on. See scanLeft
for intermediate results.
When the collection is empty, the initial value is returned.
Interface: FoldLeftable
Signature: Collection::foldLeft(callable $callback, mixed $initial): mixed;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
$callback = static fn (int $carry, int $item): int => $carry + $item;
$collection = Collection::fromIterable(range(1, 5))
->foldLeft($callback, 0); // 15
$collection = Collection::empty()
->foldLeft($callback, 'foo'); // 'foo'
foldLeft1¶
Takes the first two items of the list and applies the function to them, then
feeds the function with this result and the third argument and so on.
See scanLeft1
for intermediate results.
Interface: FoldLeft1able
Signature: Collection::foldLeft1(callable $callback): mixed;
$callback = static fn(int $carry, int $value): int => $carry - $value;
Collection::fromIterable([64, 4, 2, 8])
->foldLeft1($callback); // 50
Collection::empty()
->foldLeft1($callback); // null
foldRight¶
Takes the initial value and the last item of the list and applies the function,
then it takes the penultimate item from the end and the result, and so on.
See scanRight
for intermediate results.
Interface: FoldRightable
Signature: Collection::foldRight(callable $callback, mixed $initial): mixed;
Collection::fromIterable(range('A', 'C'))
->foldLeft(
static function (string $carry, string $item): string {
$carry .= $item;
return $carry;
},
''
); // 'CBA'
foldRight1¶
Takes the last two items of the list and applies the function, then it takes the
third item from the end and the result, and so on. See scanRight1
for
intermediate results.
Interface: FoldRight1able
Signature: Collection::foldRight1(callable $callback): mixed;
$callback = static fn(int $carry, int $value): int => $carry + $value;
Collection::fromIterable([1, 2, 3, 4])
->foldRight1($callback); // 10
Collection::empty()
->foldRight1($callback); // null
forget¶
Remove items having specific keys.
Interface: Forgetable
Signature: Collection::forget(mixed ...$keys): Collection;
$collection = Collection::fromIterable(range('a', 'e'))
->forget(0, 4); // [1 => 'b', 2 => 'c', 3 => 'd']
frequency¶
Calculate the frequency of the items in the collection
Returns a new key-value collection with frequencies as keys.
Interface: Frequencyable
Signature: Collection::frequency(): Collection;
$collection = Collection::fromIterable(['a', 'b', 'c', 'b', 'c', 'c'])
->frequency()
->all(false); // [1 => 'a', 2 => 'b', 3 => 'c'];
get¶
Get a specific element of the collection from a key; if the key doesn’t exist, returns the default value.
Interface: Getable
Signature: Collection::get(mixed $key, mixed $default = null): Collection;
Collection::fromIterable(range('a', 'c'))->get(1) // 'b'
Collection::fromIterable(range('a', 'c'))->get(4, '') // ''
group¶
Takes a list and returns a list of lists such that the concatenation of the result is equal to the argument. Moreover, each sublist in the result contains only equal elements.
Interface: Groupable
Signature: Collection::group(): Collection;
Collection::fromString('Mississippi')
->group(); // [ [0 => 'M'], [1 => 'i'], [2 => 's', 3 => 's'], [4 => 'i'], [5 => 's', 6 => 's'], [7 => 'i'], [8 => 'p', 9 => 'p'], [10 => 'i'] ]
groupBy¶
Group items based on a custom callback. The grouping will be based on the returned value of the callback. The callback takes two parameters, the value and the key of the current item in the iterator, the returned value must be an integer or a string.
Interface: GroupByable
Signature: Collection::groupBy(callable $callback): Collection;
<?php
declare(strict_types=1);
namespace App;
use Generator;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
$generator = static function (): Generator {
yield 1 => 'a';
yield 1 => 'b';
yield 1 => 'c';
yield 2 => 'd';
yield 2 => 'e';
yield 3 => 'f';
};
$groupByCallback = static fn (string $char, int $key): int => $key;
$collection = Collection::fromIterable($generator())
->groupBy($groupByCallback); // [1 => ['a', 'b', 'c'], 2 => ['d', 'e'], 3 => ['f']]
has¶
Check if the collection has values with the help of one or more callables.
Warning
The callbacks
parameter is variadic and will be evaluated as a logical OR
.
Interface: Hasable
Signature: Collection::has(callable ...$callbacks): bool;
$result = Collection::fromIterable(range('A', 'C'))
->has(static fn (): string => 'B'); // true
$result = Collection::fromIterable(range('A', 'C'))
->has(static fn (): string => 'D'); // false
$result = Collection::fromIterable(range('A', 'C'))
->has(
static fn ($value, $key): string => $key > 4 ? 'D' : 'A',
static fn ($value, $key): string => 'Z'
); // true
head¶
Get the first item from the collection in a separate collection. Same as first
.
Interface: Headable
Signature: Collection::head(mixed $default = null): mixed;
$generator = static function (): Generator {
yield 1 => 'a';
yield 1 => 'b';
yield 1 => 'c';
yield 2 => 'd';
yield 2 => 'e';
yield 3 => 'f';
};
Collection::fromIterable($generator())
->head(); // 'a'
ifThenElse¶
Execute a mapping callback on each item of the collection when a condition is met.
If no else
callback is provided, the identity function is applied (elements are not modified).
Interface: IfThenElseable
Signature: Collection::ifThenElse(callable $condition, callable $then, ?callable $else = null): Collection;
$input = range(1, 5);
$condition = static function (int $value): bool {
return 0 === $value % 2;
};
$then = static function (int $value): int {
return $value * $value;
};
$else = static function (int $value): int {
return $value + 2;
};
Collection::fromIterable($input)
->ifThenElse($condition, $then); // [1, 4, 3, 16, 5]
Collection::fromIterable($input)
->ifThenElse($condition, $then, $else) // [3, 4, 5, 16, 7]
implode¶
Join all the elements of the collection into a single string using a glue provided or the empty string as default.
Interface: Implodeable
Signature: Collection::implode(string $glue = ''): string;
Collection::fromIterable(range('a', 'c'))
->implode('-'); // 'a-b-c'
init¶
Returns the collection without its last item.
Interface: Initable
Signature: Collection::init(): Collection;
Collection::fromIterable(range('a', 'e'))
->init(); // ['a', 'b', 'c', 'd']
inits¶
Returns all initial segments of the collection, shortest first.
Interface: Initsable
Signature: Collection::inits(): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
$input = range('a', 'c');
Collection::fromIterable(range('a', 'c'))
->inits(); // [[], [[0, 'a']], [[0, 'a'], [1, 'b']], [[0, 'a'], [1, 'b'], [2, 'c']]]
Collection::fromIterable(array_combine(range('A', 'C'), $input))
->inits(); // [[], [['A', 'a']], [['A', 'a'], ['B', 'b']], [['A', 'a'], ['B', 'b'], ['C', 'c']]]
// To get only the values:
// Using `map` + `array_column`
Collection::fromIterable($input)
->inits()
->map(static fn (array $data): array => array_column($data, 1));
// [[], ['a'], ['a', 'b'], ['a', 'b', 'c']]
// Using the `pluck` operation
$var = Collection::fromIterable($input)
->inits()
->pluck('*.1');
// [[], ['a'], ['a', 'b'], ['a', 'b', 'c']]
intersect¶
Removes any values from the original collection that are not present in the given values set.
Interface: Intersectable
Signature: Collection::intersect(mixed ...$values): Collection;
$collection = Collection::fromIterable(range('a', 'e'))
->intersect('a', 'b', 'c'); // ['a', 'b', 'c']
intersectKeys¶
Removes any keys from the original collection that are not present in the given keys set.
Interface: Intersectkeysable
Signature: Collection::intersectKeys(mixed ...$keys): Collection;
$collection = Collection::fromIterable(range('a', 'e'))
->intersectKeys(0, 2, 4); // ['a', 'c', 'e']
intersperse¶
Insert a given value at every n
element of a collection; indices are not preserved.
Interface: Intersperseable
Signature: Collection::intersperse(mixed $element, int $every = 1, int $startAt = 0): Collection;
$collection = Collection::fromIterable(range('a', 'c'))
->intersperse('x');
foreach($collection as $item) {
var_dump($item); // 'x', 'a', 'x', 'b', 'x', 'c'
}
isEmpty¶
Check if a collection has no elements inside.
Interface: IsEmptyable
Signature: Collection::isEmpty(): bool;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
Collection::fromIterable(range('a', 'c'))
->isEmpty(); // false
Collection::fromIterable([])
->isEmpty(); // true
Collection::fromIterable([null])
->isEmpty(); // false
isNotEmpty¶
Check if a collection has at least one element inside.
Interface: IsNotEmptyable
Signature: Collection::isNotEmpty(): bool;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
Collection::fromIterable(range('a', 'c'))
->isNotEmpty(); // true
Collection::fromIterable([])
->isNotEmpty(); // false
Collection::fromIterable([null])
->isNotEmpty(); // true
jsonSerialize¶
Returns the collection items as an array, allowing serialization. Essentially calls all(false)
,
which means the collection is not normalized by default.
See the section on serialization.
Interface: JsonSerializable
Signature: Collection::jsonSerialize(): array;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
Collection::fromIterable([1, 2, 3])
->jsonSerialize(); // [1, 2, 3]
Collection::fromIterable([1, 2, 3])
->filter(static fn (int $val): bool => $val % 2 !== 0)
->jsonSerialize(); // [0 => 1, 2 => 3]
Collection::fromIterable(['foo' => 1, 'bar' => 2])
->jsonSerialize(); // ['foo' => 1, 'bar' => 2]
key¶
Get the key of an item in the collection given a numeric index, default index is 0.
Interface: Keyable
Signature: Collection::key(int $index = 0);
Collection::fromIterable(['a', 'b', 'c', 'd'])->key(); // Return 0
Collection::fromIterable(['a', 'b', 'c', 'd'])->key(0); // Return 0
Collection::fromIterable(['a', 'b', 'c', 'd'])->key(1); // Return 1
Collection::fromIterable(['a', 'b', 'c', 'd'])->key(10); // Return null
keys¶
Get the keys of the items.
Interface: Keysable
Signature: Collection::keys(): Collection;
$collection = Collection::fromIterable(range('a', 'd'))
->keys(); // [0, 1, 2, 3]
last¶
Extract the last element of a collection.
Interface: Lastable
Signature: Collection::last(mixed $default = null): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
Collection::fromIterable(['a', 'b', 'c'])
->last(); // 'c'
Collection::fromIterable(['a', 'b', 'c'])
->last('default_value'); // 'c'
Collection::empty()
->last('default_value'); // 'default_value'
limit¶
Limit the number of values in the collection.
Interface: Limitable
Signature: Collection::limit(int count = -1, int $offset = 0): Collection;
$even = Collection::range(0, \INF, 2)
->limit(5); // [0, 2, 4, 6, 8]
lines¶
Split a string into lines.
Interface: Linesable
Signature: Collection::lines(): Collection;
$string = <<<'EOF'
The quick brown fox jumps over the lazy dog.
This is another sentence.
EOF;
Collection::fromString($string)
->lines(); // ['The quick brown fox jumps over the lazy dog.', '', 'This is another sentence.']
map¶
Apply a single callback to every item of a collection and use the return value.
Warning
An earlier version of this operation allowed usage with multiple callbacks. This behaviour
was removed in version 5.0
; mapN
should be used instead, or,
alternatively, multiple successive map
calls can achieve the same result.
Warning
Keys are preserved, use the Collection::normalize
operation if you want to re-index the keys.
Interface: Mapable
Signature: Collection::map(callable $callback): Collection;
$square = static fn (int $val): int => $val ** 2;
$toString = static fn (int $val): string => (string) $val;
$appendBar = static fn (int $val): string => $val . 'bar';
$collection = Collection::fromIterable(range(1, 3))
->map($square)
->map($toString)
->map($appendBar); // ['1bar', '4bar', '9bar']
mapN¶
Apply one or more callbacks to every item of a collection and use the return value.
Tip
This operation is best used when multiple callbacks need to be applied. If you only want to apply
a single callback, map
should be preferred as it benefits from more specific type hints.
Warning
Keys are preserved, use the Collection::normalize
operation if you want to re-index the keys.
Interface: MapNable
Signature: Collection::mapN(callable ...$callbacks): Collection;
$square = static fn (int $val): int => $val ** 2;
$toString = static fn (int $val): string => (string) $val;
$appendBar = static fn (int $val): string => $val . 'bar';
$collection = Collection::fromIterable(range(1, 3))
->mapN($square, $toString, $appendBar); // ['1bar', '4bar', '9bar']
match¶
Check if the collection matches a given user callback
.
You must provide a callback that can get the key
, the current value
, and the iterator
as parameters.
When no matcher callback is provided, the user callback must return true
(the
default value of the matcher callback
) in order to stop.
The returned value of the operation is true
when the callback matches at least one element
of the collection, false
otherwise.
If you want to match the user callback
against another value (other than true
), you must
provide your own matcher callback
as a second argument, and it must return a boolean
.
Interface: Matchable
Signature: Collection::match(callable $callback, ?callable $matcher = null): bool;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
// Example 1 -> match found
$result = Collection::fromIterable(range(1, 100))
->match(static fn (int $value): bool => 10 > $value); // true
// Example 2 -> match not found
$result = Collection::fromIterable(range(1, 100))
->match(static fn (int $value): bool => 314 < $value); // false
// Example 3 -> match found
$input = [
'Ningbo (CN)',
'California (US)',
'Brussels (EU)',
'New York (US)',
];
$pattern = '/\(EU\)$/';
$result = Collection::fromIterable($input)
->match(static fn (string $value): bool => (bool) preg_match($pattern, $value)); // true
// Example 4 -> custom matcher
$result = Collection::fromIterable(range(1, 10))
->match(static fn (int $value): bool => 5 !== $value, static fn (): bool => false); // true
matching¶
Collection lets you use the Criteria API provided by Doctrine Collections, but in a lazy way.
Interface: Matchingable
Signature: Collection::matching(Criteria $criteria): Collection;
<?php
declare(strict_types=1);
namespace App;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\EntityManagerInterface;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
/** @var EntityManagerInterface $em */
$q = $em->createQuery('SELECT u FROM MyProject\Model\User u');
// 1. Injecting the query resultset in a collection
$collection = Collection::fromIterable($q->toIterable());
// 2. Using a criteria
$collection = Collection::fromIterable($q->toIterable())
->matching(
Criteria::create()
->where(
Criteria::expr()
->eq('isActive', true)
)
);
max¶
Generate the maximum value of the collection by successively applying the PHP max
function
to each pair of two elements.
If custom logic is required to determine the maximum, such as when comparing objects,
the compare
operation can be used instead.
Interface: Maxable
Signature: Collection::max(mixed $default = null): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
use const PHP_INT_MAX;
include __DIR__ . '/../../../../vendor/autoload.php';
$result = Collection::fromIterable([1, 4, 3, 0, 2])
->max(); // 4
$result = Collection::fromIterable([1, 4, null, 0, 2])
->max(); // 4
$result = Collection::empty()
->max(PHP_INT_MAX); // PHP_INT_MAX
merge¶
Merge one or more iterables onto a collection.
Interface: Mergeable
Signature: Collection::merge(iterable ...$sources): Collection;
Collection::fromIterable(range(1, 5))
->merge(range(6, 10)); // range(1, 10)
$collection = Collection::fromIterable(['a', 'b', 'c'])
->merge(Collection::fromIterable(['d', 'e']);
$collection->all() // ['a', 'b', 'c', 'd', 'e']
min¶
Generate the minimum value of the collection by successively applying the PHP min
function
to each pair of two elements.
If custom logic is required to determine the minimum, such as when comparing objects,
the compare
operation can be used instead.
Interface: Minable
Signature: Collection::min(mixed $default = null): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
use const PHP_INT_MIN;
include __DIR__ . '/../../../../vendor/autoload.php';
$result = Collection::fromIterable([1, 4, 3, 0, 2])
->min(); // 0
$result = Collection::fromIterable([1, 4, null, 0, 2])
->min(); // null
$result = Collection::empty()
->min(PHP_INT_MIN); // PHP_INT_MIN
normalize¶
Replace, reorder and use numeric keys on a collection.
Interface: Normalizeable
Signature: Collection::normalize(): Collection;
<?php
declare(strict_types=1);
namespace App;
use Generator;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
// Example 1 -> string keys
$collection = Collection::fromIterable(['a' => 10, 'b' => 100, 'c' => 1000])
->normalize(); // [0 => 10, 1 => 100, 2 => 1000]
// Example 2 -> duplicate keys
$generator = static function (): Generator {
yield 1 => 'a';
yield 2 => 'b';
yield 1 => 'c';
yield 3 => 'd';
};
$collection = Collection::fromIterable($generator())
->normalize(); // [0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd']
// Example 3 -> "missing" numeric keys
$collection = Collection::fromIterable(range(1, 5))
->filter(static fn (int $val): bool => $val % 2 === 0) // [1 => 2, 3 => 4]
->normalize(); // [0 => 2, 1 => 4]
nth¶
Get every n-th element of a collection.
Interface: Nthable
Signature: Collection::nth(int $step, int $offset = 0): Collection;
$collection = Collection::fromIterable(range(1, 20))
->nth(5); // [0 => 1, 5 => 6, 10 => 11, 15 => 16]
nullsy¶
Check if the collection contains only nullsy values.
Nullsy values are:
The null value:
null
Empty array:
[]
The integer zero:
0
The boolean:
false
The empty string:
''
Interface: Nullsyable
Signature: Collection::nullsy(): bool;
$result = Collection::fromIterable([null, false])
->nullsy(); // true
$result = Collection::fromIterable(['a', null, 'c'])
->nullsy(); // false
pack¶
Wrap each item into an array containing 2 items: the key and the value.
Interface: Packable
Signature: Collection::pack(): Collection;
$input = ['a' => 'b', 'c' => 'd', 'e' => 'f'];
$c = Collection::fromIterable($input)
->pack();
// [
// ['a', 'b'],
// ['c', 'd'],
// ['e', 'f'],
// ]
pad¶
Pad a collection to the given length with a given value.
Interface: Padable
Signature: Collection::pad(int $size, mixed $value): Collection;
$collection = Collection::fromIterable(range(1, 5))
->pad(8, 'foo'); // [1, 2, 3, 4, 5, 'foo', 'foo', 'foo']
pair¶
Make an associative collection from pairs of values.
Interface: Pairable
Signature: Collection::pair(): Collection;
$input = [
[
'key' => 'k1',
'value' => 'v1',
],
[
'key' => 'k2',
'value' => 'v2',
],
[
'key' => 'k3',
'value' => 'v3',
],
[
'key' => 'k4',
'value' => 'v4',
],
[
'key' => 'k4',
'value' => 'v5',
],
];
$c = Collection::fromIterable($input)
->unwrap()
->pair();
// [
// [k1] => v1
// [k2] => v2
// [k3] => v3
// [k4] => [
// [0] => v4
// [1] => v5
// ]
// ]
partition¶
Partition the collection into two subgroups of items using one or more callables.
Warning
The callbacks parameter is variadic and will be evaluated as a logical OR
.
If you’re looking for a logical AND
, you have to make multiple calls to the
same operation.
The raw Partition
operation returns a generator yielding two iterators.
The first inner iterator is the result of a filter
operation, it contains items
that have met the provided callback(s).
The second (and last) inner iterator is the result of a reject
operation, it contains items
that have not met the provided callback(s).
When the partition
operation is used through the Collection
object, the two
resulting iterators will be converted and mapped into a Collection
object.
The first inner collection contains items that have met the provided callback(s). The second (and last) collection contains items that have not met the provided callback(s).
Interface: Partitionable
Signature: Collection::partition(callable ...$callbacks): Collection;
<?php
declare(strict_types=1);
namespace App;
use Closure;
use loophp\collection\Collection;
use loophp\collection\Operation\Partition;
include __DIR__ . '/../../../../vendor/autoload.php';
$isGreaterThan = static fn (int $left): Closure => static fn (int $right): bool => $left < $right;
$input = array_combine(range('a', 'l'), [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3]);
// Example 1 -> Retrieve the left and right groups
[$left, $right] = Collection::fromIterable($input)
->partition($isGreaterThan(5))
->all();
// Numbers that are greater than 5
print_r($left->all(false));
/*
[
['f', 6],
['g', 7],
['h', 8],
['i', 9],
]
*/
// Numbers that are not greater than 5
print_r($right->all(false));
/*
[
['a', 1],
['b', 2],
['c', 3],
['d', 4],
['e', 5],
['j', 1],
['k', 2],
['l', 3],
]
*/
// Example 2 -> Retrieve the first group only
$left = Collection::fromIterable($input)
->partition($isGreaterThan(5))
->first();
// Numbers that are greater than 5
print_r($left->all(false));
/*
[
['f', 6],
['g', 7],
['h', 8],
['i', 9],
]
*/
// Example 3 -> Use Partition operation separately
[$left] = iterator_to_array(Partition::of()($isGreaterThan(5))($input));
// Numbers that are greater than 5
print_r(iterator_to_array($left));
/*
[
['f', 6],
['g', 7],
['h', 8],
['i', 9],
]
*/
permutate¶
Find all the permutations of a collection.
Interface: Permutateable
Signature: Collection::permutate(): Collection;
$collection = Collection::fromIterable(['a', 'b'])
->permutate(); // [['a', 'b'], ['b', 'a']]
pipe¶
Pipe together multiple operations and apply them in succession to the collection items.
To maintain a lazy nature, each operation needs to return a Generator
.
Custom operations and operations provided in the API can be combined together.
Interface: Pipeable
Signature: Collection::pipe(callable ...$callbacks): Collection;
<?php
declare(strict_types=1);
namespace App;
use Closure;
use Generator;
use loophp\collection\Collection;
use loophp\collection\Operation\AbstractOperation;
use loophp\collection\Operation\Reverse;
include __DIR__ . '/../../../../vendor/autoload.php';
$square = static function ($collection): Generator {
foreach ($collection as $item) {
yield $item ** 2;
}
};
$toString = static function ($collection): Generator {
foreach ($collection as $item) {
yield (string) $item;
}
};
$times = new class() extends AbstractOperation {
public function __invoke(): Closure
{
return static function ($collection): Generator {
foreach ($collection as $item) {
yield "{$item}x";
}
};
}
};
Collection::fromIterable(range(1, 5))
->pipe($square, Reverse::of(), $toString, $times())
->all(); // ['25x', '16x', '9x', '4x', '1x']
pluck¶
Retrieves all of the values of a collection for a given key.
Nested values can be retrieved using “dot notation” and the wildcard character *
.
Interface: Pluckable
Signature: Collection::pluck(mixed $pluck, mixed $default = null): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
// Example 1 -> numeric keys
$fibonacci = static fn ($a = 0, $b = 1): array => [$b, $a + $b];
$collection = Collection::unfold($fibonacci)
->limit(6)
->pluck(0); // [1, 1, 2, 3, 5, 8]
// Example 2 -> basic string keys
$input = [
['foo' => 'A', 'bar' => 'B'],
['foo' => 'C', 'bar' => 'D'],
];
$collection = Collection::fromIterable($input)
->pluck('foo'); // ['A', 'C']
// Example 3 -> nested keys and values
$input = [
['foo' => 'A', 'bar' => ['baz' => 'B']],
['foo' => 'C', 'bar' => ['baz' => 'D']],
];
$collection = Collection::fromIterable($input);
$collection->pluck('bar'); // [['baz' => 'B'], ['baz' => 'D']]
$collection->pluck('bar.baz'); // ['B', 'D']
$collection->pluck('*.baz'); // [[null, 'B'], [null, 'D']]
prepend¶
Push an item onto the beginning of the collection.
Warning
This operation maintains the keys of the prepended items. If you wish to re-index the keys you can use the
Collection::normalize()
operation, or Collection::all()
when converting into an array, which will apply
normalize
by default.
Interface: Prependable
Signature: Collection::prepend(mixed ...$items): Collection;
Collection::fromIterable([1 => '1', 2 => '2', 3 => '3'])
->prepend('4'); // [0 => 4, 1 => '1', 2 => '2', 3 => '3']
Collection::fromIterable(['1', '2', '3'])
->prepend('4')
->prepend('5', '6')
->all(); // ['5', '6', '4', '1', '2', '3']
product¶
Get the cartesian product of items of a collection.
Interface: Productable
Signature: Collection::product(iterable ...$iterables): Collection;
$collection = Collection::fromIterable(range('A', 'C'))
->product([1, 2]); // [['A', 1], ['A', 2], ['B', 1], ['B', 2], ['C', 1], ['C', 2]]
random¶
Returns a random item from the collection.
An optional integer can be passed to random to specify how many items you would like to randomly retrieve. An optional seed can be passed as well.
Interface: Randomable
Signature: Collection::random(int $size = 1, ?int $seed = null): Collection;
$collection = Collection::fromIterable(['4', '5', '6'])
->random(); // ['6']
reduce¶
Reduce a collection of items through a given callback. An optional initial value is by default set to null and can be customized by the user through the second argument.
When the collection is empty, the initial value is returned.
Interface: Reduceable
Signature: Collection::reduce(callable $callback, mixed $initial = null): mixed;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
$callback = static fn (int $carry, int $item): int => $carry + $item;
$collection = Collection::fromIterable(range(1, 5))
->reduce($callback, 0); // 15
$collection = Collection::empty()
->reduce($callback, 'foo'); // 'foo'
$collection = Collection::empty()
->reduce($callback); // null
reduction¶
Takes the initial value and the first item of the list and applies the function to them, then feeds the function with this result and the second argument and so on. It returns the list of intermediate and final results.
When the collection is empty, the initial value is yielded.
Interface: Reductionable
Signature: Collection::reduction(callable $callback, mixed $initial = null): Collection;
$callback = static fn ($carry, $item) => $carry + $item;
$collection = Collection::fromIterable(range(1, 5))
->reduction($callback); // [1, 3, 6, 10, 15]
reject¶
Reject collection items based on one or more callbacks.
Warning
The callbacks parameter is variadic and will be evaluated as a logical OR
.
If you’re looking for a logical AND
, you have to make multiple calls to the
same operation. However, due to the nature of this operation, the behaviour is the same.
Tip
It is only when the callback returns false
that the value is kept.
Tip
If you’re looking for keeping the value in the iterator when the return is true
, see the filter
operation.
Interface: Rejectable
Signature: Collection::reject(callable ...$callbacks): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
$divisibleBy2 = static fn ($value): bool => 0 === $value % 2;
$divisibleBy3 = static fn ($value): bool => 0 === $value % 3;
// Reject values divisible by 2
$collection = Collection::fromIterable(range(1, 10))
->reject($divisibleBy2); // [1, 3, 5, 7, 9]
// Reject values divisible by 2 or 3
$collection = Collection::fromIterable(range(1, 10))
->reject($divisibleBy2, $divisibleBy3); // [1, 5, 7]
// Reject values not divisible by 2 and then by 3
$collection = Collection::fromIterable(range(1, 10))
->reject($divisibleBy2)
->reject($divisibleBy3); // [1, 5, 7]
reverse¶
Reverse the order of items in a collection.
Interface: Reverseable
Signature: Collection::reverse(): Collection;
$collection = Collection::fromIterable(['a', 'b', 'c'])
->reverse(); // ['c', 'b', 'a']
rsample¶
Take a random sample of elements of items from a collection. Accepts a probability parameter which will influence the number of items sampled - higher probabilities increase the chance of sampling close to the entire collection.
Interface: RSampleable
Signature: Collection::rsample(float $probability): Collection;
$collection = Collection::fromIterable(range(1, 5));
$collection->rsample(1.0); // [1, 2, 3, 4, 5]
$collection->rsample(0.5); // will get about half of the elements at random
same¶
Compare two collections for sameness. Collections are considered same if:
they have the same number of elements;
they have the same keys and elements, in the same order.
By default elements and keys will be compared using strict equality (===
). However,
this behaviour can be customized with a comparator callback. This should be a curried function
which takes first the left value and key, then the right value and key, and returns a boolean.
This operation will stop and return a value as soon as one of the collections has been seen fully or as soon as the comparison yields false for any key-value pair.
Interface: Sameable
Signature: Collection::same(Collection $other, ?callable $comparatorCallback = null): bool;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
use stdClass;
include __DIR__ . '/../../../../vendor/autoload.php';
Collection::fromIterable([1, 2, 3])
->same(Collection::fromIterable([1, 2, 3])); // true
Collection::fromIterable([1, 2, 3])
->same(Collection::fromIterable([3, 1, 2])); // false
Collection::fromIterable([1, 2, 3])
->same(Collection::fromIterable([1, 2])); // false
Collection::fromIterable([1, 2, 3])
->same(Collection::fromIterable([1, 2, 4])); // false
Collection::fromIterable(['foo' => 'f'])
->same(Collection::fromIterable(['foo' => 'f'])); // true
Collection::fromIterable(['foo' => 'f'])
->same(Collection::fromIterable(['bar' => 'f'])); // false
Collection::fromIterable(['foo' => 'f', 'bar' => 'b'])
->same(Collection::fromIterable(['foo' => 'f', 'bar' => 'b'])); // true
Collection::fromIterable(['foo' => 'f', 'bar' => 'b'])
->same(Collection::fromIterable(['bar' => 'b', 'foo' => 'f'])); // false
$a = (object) ['id' => 'a'];
$a2 = (object) ['id' => 'a'];
Collection::fromIterable([$a])
->equals(Collection::fromIterable([$a])); // true
Collection::fromIterable([$a])
->equals(Collection::fromIterable([$a2])); // false
$comparator = static fn (string $left) => static fn (string $right): bool => $left === $right;
Collection::fromIterable(['foo' => 'f'])
->same(Collection::fromIterable(['bar' => 'f']), $comparator); // true
$comparator = static fn ($left, $leftKey) => static fn ($right, $rightKey): bool => $left === $right
&& mb_strtolower($leftKey) === mb_strtolower($rightKey);
Collection::fromIterable(['foo' => 'f'])
->same(Collection::fromIterable(['FOO' => 'f']), $comparator); // true
$comparator = static fn (stdClass $left) => static fn (stdClass $right): bool => $left->id === $right->id;
Collection::fromIterable([$a])
->same(Collection::fromIterable([$a2]), $comparator); // true
scale¶
Scale/normalize values.
Values will be scaled between 0
and 1
by default, if no desired bounds are provided.
Interface: Scaleable
Signature: Collection::scale(float $lowerBound, float $upperBound, float $wantedLowerBound = 0.0, float $wantedUpperBound = 1.0, float $base = 0.0): Collection;
$default = Collection::fromIterable([0, 5, 10, 15, 20])
->scale(0, 20); // [0, 0.25, 0.5, 0.75, 1]
$withBounds = Collection::fromIterable([0, 5, 10, 15, 20])
->scale(0, 20, 0, 12); // [0, 3, 6, 9, 12]
$withBoundsAndBase = Collection::fromIterable([0, 5, 10, 15, 20])
->scale(0, 20, 0, 12, \M_E); // [1, 6.90, 9.45, 10.94, 12]
scanLeft¶
Takes the initial value and the first item of the list and applies the function to them, then feeds the function with this result and the second argument and so on. It returns the list of intermediate and final results.
When the collection is empty, the initial value is yielded.
Interface: ScanLeftable
Signature: Collection::scanLeft(callable $callback, mixed $initial): Collection;
$callback = static function ($carry, $value) {
return $carry / $value;
};
Collection::fromIterable([4, 2, 4])
->scanLeft($callback, 64)
->normalize(); // [64, 16, 8, 2]
Collection::empty()
->scanLeft($callback, 3); // [0 => 3]
scanLeft1¶
Takes the first two items of the list and applies the function to them, then feeds the function with this result and the third argument and so on. It returns the list of intermediate and final results.
Warning
You might need to use the normalize
operation after this.
Interface: ScanLeft1able
Signature: Collection::scanLeft1(callable $callback): Collection;
$callback = static function ($carry, $value) {
return $carry / $value;
};
Collection::fromIterable([64, 4, 2, 8])
->scanLeft1($callback); // [64, 16, 8, 1]
Collection::fromIterable([12])
->scanLeft1($callback); // [12]
Collection::empty()
->scanLeft1($callback); // []
scanRight¶
Takes the initial value and the last item of the list and applies the function, then it takes the penultimate item from the end and the result, and so on. It returns the list of intermediate and final results.
When the collection is empty, the initial value is yielded.
Interface: ScanRightable
Signature: Collection::scanRight(callable $callback, mixed $initial): Collection;
$callback = static function ($carry, $value) {
return $value / $carry;
};
Collection::fromIterable([8, 12, 24, 4])
->scanRight($callback, 2); // [8, 1, 12, 2, 2]
Collection::empty()
->scanRight($callback, 3); // [3]
scanRight1¶
Takes the last two items of the list and applies the function, then it takes the third item from the end and the result, and so on. It returns the list of intermediate and final results.
Warning
You might need to use the normalize
operation after this.
Interface: ScanRight1able
Signature: Collection::scanRight1(callable $callback): Collection;
$callback = static function ($carry, $value) {
return $value / $carry;
};
Collection::fromIterable([8, 12, 24, 2])
->scanRight1($callback); // [8, 1, 12, 2]
Collection::fromIterable([12])
->scanRight1($callback); // [12]
Collection::empty()
->scanRight1($callback); // []
shuffle¶
Shuffle a collection, randomly changing the order of items.
Interface: Shuffleable
Signature: Collection::shuffle(?int $seed = null): Collection;
$collection = Collection::fromIterable(['4', '5', '6'])
->random(); // ['6', '4', '5']
$collection = Collection::fromIterable(['4', '5', '6'])
->random(); // ['5', '6', '5']
since¶
Skip items until the callback is met.
Warning
The callbacks parameter is variadic and will be evaluated as a logical OR
.
If you’re looking for a logical AND
, you have to make multiple calls to the
same operation.
Interface: Sinceable
Signature: Collection::since(callable ...$callbacks): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
use loophp\collection\Contract\Operation\Splitable;
include __DIR__ . '/../../../../vendor/autoload.php';
// Example 1 -> Parse the composer.json of a package and get the require-dev dependencies.
/** @var resource $fileResource */
$fileResource = fopen(__DIR__ . '/composer.json', 'rb');
$collection = Collection::fromResource($fileResource)
// Group items when EOL character is found.
->split(Splitable::REMOVE, static fn (string $character): bool => "\n" === $character)
// Implode characters to create a line string
->map(static fn (array $characters): string => implode('', $characters))
// Skip items until the string "require-dev" is found.
->since(static fn ($line): bool => str_contains($line, 'require-dev'))
// Skip items after the string "}" is found.
->until(static fn ($line): bool => str_contains($line, '}'))
// Re-index the keys
->normalize()
// Filter out the first line and the last line.
->filter(
static fn ($line, $index): bool => 0 !== $index,
static fn ($line): bool => !str_contains($line, '}')
)
// Trim remaining results and explode the string on ':'.
->map(
static fn ($line) => trim($line),
static fn ($line) => explode(':', $line)
)
// Take the first item.
->pluck(0)
// Convert to array.
->all();
// Example 2 -> Usage as logical OR and logical AND
$input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3];
$isGreaterThanThree = static fn (int $value): bool => 3 < $value;
$isGreaterThanEight = static fn (int $value): bool => 8 < $value;
$logicalORCollection = Collection::fromIterable($input)
->since($isGreaterThanThree, $isGreaterThanEight);
// [4, 5, 6, 7, 8, 9, 1, 2, 3]
$logicalANDCollection = Collection::fromIterable($input)
->since($isGreaterThanThree)
->since($isGreaterThanEight);
// [9, 1, 2, 3]
slice¶
Get a slice of a collection.
Interface: Sliceable
Signature: Collection::slice(int $offset, ?int $length = -1): Collection;
$collection = Collection::fromIterable(range('a', 'z'))
->slice(5, 3); // [5 => 'f', 6 => 'g', 7 => 'h']
sort¶
Sort a collection using a callback. If no callback is provided, it will sort using natural order. The direction by default match the PHP usort function.
By default, it will sort by values and using the default callback. If you want to sort by keys, you can pass a parameter to change the behaviour.
Since version 7.4, sorting is stable by default. Stable sort algorithms sort equal elements in the same order that they appear in the input.
Interface: Sortable
Signature: Collection::sort(int $type = Sortable::BY_VALUES, ?callable $callback = null): Collection;
Callback signature: Closure(mixed $right, mixed $left, mixed $rightKey, mixed $leftKey): int
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
use loophp\collection\Contract\Operation\Sortable;
include __DIR__ . '/../../../../vendor/autoload.php';
// Example 1 -> Regular values sorting
$collection = Collection::fromIterable(['z', 'y', 'x'])
->sort(); // [2 => 'x', 1 => 'y', 0 => 'z']
// Example 2 -> Regular values sorting with a custom callback
$collection = Collection::fromIterable(['z', 'y', 'x'])
->sort(
Sortable::BY_VALUES,
static fn (string $left, string $right): int => $left <=> $right
); // [0 => 'z', 1 => 'y', 2 => 'x']
// Example 3 -> Regular values sorting with a custom callback, inverted
$collection = Collection::fromIterable(['z', 'y', 'x'])
->sort(
Sortable::BY_VALUES,
static fn (string $left, string $right): int => $right <=> $left
); // [2 => 'x', 1 => 'y', 0 => 'z']
// Example 4 -> Regular keys sorting (no callback is needed here)
$collection = Collection::fromIterable([3 => 'z', 2 => 'y', 1 => 'x'])
->sort(Sortable::BY_KEYS); // [1 => 'x', 2 => 'y', 3 => 'z']
// Example 5 -> Regular keys sorting using the flip() operation twice
$collection = Collection::fromIterable([3 => 'z', 2 => 'y', 1 => 'x'])
->flip() // Exchange values and keys
->sort() // Sort the values (which are now the keys)
->flip(); // Flip again to put back the keys and values, sorted by keys.
// [1 => 'x', 2 => 'y', 3 => 'z']
span¶
Partition the collection into two subgroups where the first element is the longest prefix (possibly empty) of elements that satisfy the callback(s) and the second element is the remainder.
The raw Span
operation returns a generator yielding two iterators.
The first inner iterator is the result of a TakeWhile
operation.
The second (and last) inner iterator is the result of a DropWhile
operation.
When the span
operation is used through the Collection
object, the two
resulting iterators will be converted and mapped into Collection
objects.
Interface: Spanable
Signature: Collection::span(callable ...$callbacks): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
use loophp\collection\Operation\Span;
include __DIR__ . '/../../../../vendor/autoload.php';
$input = range(1, 10);
// Example 1 -> Retrieve the left and right groups
[$first, $last] = Collection::fromIterable($input)
->span(static fn ($x): bool => 4 > $x)
->all();
print_r($first->all()); // [1, 2, 3]
print_r($last->all()); // [4, 5, 6, 7, 8, 9, 10]
// Example 2 -> Retrieve the second group only
$last = Collection::fromIterable($input)
->span(static fn ($x): bool => 4 > $x)
->last();
print_r($last->all()); // [4, 5, 6, 7, 8, 9, 10]
// Example 3 -> Use Span operation separately
[$left] = iterator_to_array(Span::of()(static fn ($x): bool => 4 > $x)($input));
print_r(iterator_to_array($left)); // [1, 2, 3]
split¶
Split a collection using one or more callbacks.
A flag must be provided in order to specify whether the value used to split the collection should be added at the end of a chunk, at the beginning of a chunk, or completely removed.
Interface: Splitable
Signature: Collection::split(int $type = Splitable::BEFORE, callable ...$callbacks): Collection;
$splitter = static function ($value): bool => 0 === $value % 3;
$collection = Collection::fromIterable(range(0, 10))
->split(Splitable::BEFORE, $splitter); // [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
$collection = Collection::fromIterable(range(0, 10))
->split(Splitable::AFTER, $splitter); [[0], [1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
$collection = Collection::fromIterable(range(0, 10))
->split(Splitable::REMOVE, $splitter); [[], [1, 2], [4, 5], [7, 8], [10]]
squash¶
Eagerly apply operations in a collection rather than lazily.
Interface: Squashable
Signature: Collection::squash(): Collection;
<?php
declare(strict_types=1);
namespace App;
use Exception;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
$results = Collection::fromIterable(range(0, 100))
->filter(static fn (int $int) => 0 === $int % 2)
->map(static fn (int $int) => 'document' . $int . '.pdf')
->map(
static function (string $doc): string {
if (!file_exists('/doc/' . $doc)) {
throw new Exception('Nonexistent file');
}
return (string) file_get_contents($doc);
}
)
->squash(); // Instantly trigger an exception if a file does not exist.
// If no exception, you can continue the processing...
$results = $results
->filter(
static fn (string $document): bool => str_contains($document, 'foobar')
);
strict¶
Enforce a single type in the collection at runtime. If the collection contains objects, they will either be expected to implement the same interfaces or be of the exact same class (no inheritance logic applies).
Note that the current logic allows arrays of any type in the collection, as well as null.
Warning
This will trigger an InvalidArgumentException
if the collection contains elements of mixed types when consumed.
Tip
The logic for determining the type of items comes from the TypedIterator. In addition, an optional callback can be provided to this operation if a different logic for type enforcement is desired.
Interface: Strictable
Signature: Collection::strict(?callable $callback = null): Collection;
<?php
declare(strict_types=1);
namespace App;
use Countable;
use loophp\collection\Collection;
use stdClass;
use function gettype;
include __DIR__ . '/../../../../vendor/autoload.php';
// Example 1 -> allowed
Collection::fromIterable(range(1, 3))
->strict()
->all(); // [1, 2, 3]
// Example 2 -> allowed
$obj1 = new stdClass();
$obj2 = new stdClass();
$obj3 = null;
Collection::fromIterable([$obj1, $obj2, $obj3])
->strict()
->all(); // [$obj1, $obj2, $obj3]
// Example 3 -> allowed
$obj1 = new class() implements Countable {
public function count(): int
{
return 0;
}
};
$obj2 = new class() implements Countable {
public function count(): int
{
return 0;
}
};
Collection::fromIterable([$obj1, $obj2])
->strict()
->all(); // [$obj1, $obj2]
// Example 4 -> allowed
$arr1 = [1, 2, 3];
$arr2 = ['foo' => 'bar'];
Collection::fromIterable([$arr1, $arr2])
->strict()
->all(); // [$arr1, $arr2]
// Example 5 -> not allowed
Collection::fromIterable([1, 'foo', 3])
->strict()
->all(); // InvalidArgumentException
// Example 6 -> not allowed + custom callback
$obj1 = new class() implements Countable {
public function count(): int
{
return 0;
}
};
$obj2 = new class() {
public function count(): int
{
return 0;
}
};
Collection::fromIterable([$obj1, $obj2])
->strict()
->all(); // InvalidArgumentException
Collection::fromIterable([$obj1, $obj2])
->strict(static fn ($value): string => gettype($value))
->all(); // [$obj1, $obj2]
tail¶
Get the collection items except the first.
Interface: Tailable
Signature: Collection::tail(): Collection;
Collection::fromIterable(['a', 'b', 'c'])
->tail(); // [1 => 'b', 2 => 'c']
tails¶
Returns the list of initial segments of the collection, shortest last.
Similar to applying tail
successively and collecting all results in one list.
Interface: Tailsable
Signature: Collection::tails(): Collection;
Collection::fromIterable(['a', 'b', 'c'])
->tails(); // [['a', 'b', 'c'], ['b', 'c'], ['c'], []]
takeWhile¶
Iterate over the collection items while the provided callback(s) are satisfied.
It stops iterating when the callback(s) are not met.
Warning
The callbacks parameter is variadic and will be evaluated as a logical OR
.
If you’re looking for a logical AND
, you have to make multiple calls to the
same operation.
Be careful, this operation is not the same as the filter
operation.
Interface: TakeWhileable
Signature: Collection::takeWhile(callable ...$callbacks): Collection;
$isSmallerThanThree = static function (int $value): bool {
return 3 > $value;
};
Collection::fromIterable([1,2,3,4,5,6,7,8,9,1,2,3])
->takeWhile($isSmallerThanThree); // [1,2]
tap¶
Execute callback(s) on each element of the collection.
Iterates on the collection items regardless of the return value of the callback.
It is a simplified version of the apply
operation, as it does not require the callback to return a boolean value, allowing shorter syntax e.g. for logging or event dispatching: ->tap(fn($value) => $events->emit(new ItemReceived($value)))
.
Interface: Tappable
Signature: Collection::tap(callable ...$callbacks): Collection;
transpose¶
Computes the transpose of a matrix.
Interface: Transposeable
Signature: Collection::transpose(): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
// Example 1 -> classic matrix transpose
$collection = Collection::fromIterable([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
->transpose(); // [1, 4, 7], [2, 5, 8], [3, 6, 9]
// Example 2 -> extract multiple keys
$records = [
[
'id' => 2135,
'first_name' => 'John',
'last_name' => 'Doe',
],
[
'id' => 3245,
'first_name' => 'Sally',
'last_name' => 'Smith',
],
[
'id' => 5342,
'first_name' => 'Jane',
'last_name' => 'Jones',
],
[
'id' => 5623,
'first_name' => 'Peter',
'last_name' => 'Doe',
],
];
$collection = Collection::fromIterable($records)
->transpose();
// [
// 'id' => [2135, 3245, 5432, 5623],
// 'first_name' => ['John', 'Sally', 'Jane', 'Peter'],
// 'last_name' => ['Doe', 'Smith', 'Jones', 'Doe']
// ]
truthy¶
Check if the collection contains only truthy values. Opposite of falsy
.
A value is determined to be truthy by applying a bool
cast.
Interface: Truthyable
Signature: Collection::truthy(): bool;
$result = Collection::fromIterable([2, 3, 4])
->truthy(); // true
$result = Collection::fromIterable(['a', '', 'c', 'd'])
->truthy(); // false
unlines¶
Opposite of lines
, creates a single string from multiple lines using PHP_EOL
as the glue.
Interface: Unlinesable
Signature: Collection::unlines(): string;
$lines = [
'The quick brown fox jumps over the lazy dog.',
'',
'This is another sentence.',
];
Collection::fromIterable($lines)
->unlines();
// 'The quick brown fox jumps over the lazy dog.
//
// This is another sentence.'
unpack¶
Opposite of pack
, transforms groupings of items representing a key and a value into actual keys and values.
Interface: Unpackable
Signature: Collection::unpack(): Collection;
$input = [['a', 'b'], ['c', 'd'], ['e', 'f']];
$c = Collection::fromIterable($input)
->unpack();
// [
// ['a' => 'b'],
// ['c' => 'd'],
// ['e' => 'f'],
// ];
unpair¶
Opposite of pair
, creates a flat list of values from a collection of key-value pairs.
Interface: Unpairable
Signature: Collection::unpair(): Collection;
$input = [
'k1' => 'v1',
'k2' => 'v2',
'k3' => 'v3',
'k4' => 'v4',
];
$c = Collection::fromIterable($input)
->unpair(); // ['k1', 'v1', 'k2', 'v2', 'k3', 'v3', 'k4', 'v4']
until¶
Iterate over the collection items until the provided callback(s) are satisfied.
Warning
The callbacks parameter is variadic and will be evaluated as a logical OR
.
If you’re looking for a logical AND
, you have to make multiple calls to the
same operation.
Interface: Untilable
Signature: Collection::until(callable ...$callbacks): Collection;
// The Collatz conjecture (https://en.wikipedia.org/wiki/Collatz_conjecture)
$collatz = static fn (int $value): array => 0 === $value % 2
? [$value / 2]
: [$value * 3 + 1];
$collection = Collection::unfold($collatz, [10])
->unwrap()
->until(static fn ($number): bool => 1 === $number);
unwindow¶
Opposite of window
, usually needed after a call to that operation.
Turns already-created windows back into a flat list.
Interface: Unwindowable
Signature: Collection::unwindow(): Collection;
// Drop all the items before finding five 9 in a row.
$input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
Collection::fromIterable($input)
->window(4)
->dropWhile(static fn (array $value): bool => $value !== [9, 9, 9, 9, 9])
->unwindow()
->drop(1)
->normalize(); // [10, 11, 12, 13, 14, 15, 16, 17, 18]
unwords¶
Opposite of words
and similar to lines
,
creates a single string from multiple strings using one space as the glue.
Interface: Unwordsable
Signature: Collection::unwords(): string;
$words = [
'The',
'quick',
'brown',
'fox',
'jumps',
'over',
'the',
'lazy',
'dog.'
];
Collection::fromIterable($words)
->unwords(); // 'The quick brown fox jumps over the lazy dog.'
unwrap¶
Opposite of wrap
, turn a collection of arrays into a flat list.
Equivalent to Collection::flatten(1)
.
Interface: Unwrapable
Signature: Collection::unwrap(): Collection;
$asociative = Collection::fromIterable([['a' => 'A'], ['b' => 'B'], ['c' => 'C']])
->unwrap(); // ['a' => 'A', 'b' => 'B', 'c' => 'C']
$list = Collection::fromIterable([['a'], ['b'], ['c']])
->unwrap(); // ['a', 'b', 'c']
unzip¶
Opposite of zip
, splits zipped items in a collection.
Interface: Unzipable
Signature: Collection::unzip(): Collection;
$a = Collection::fromIterable(['a' => 'a', 'b' => 'b', 'c' => 'c'])
->zip(['d', 'e', 'f', 'g'], [1, 2, 3, 4, 5]);
$b = Collection::fromIterable($a)
->unzip(); // [ ['a','b','c',null,null], ['d','e','f','g',null], [1,2,3,4,5] ]
when¶
This operation will execute the given $whenTrue
callback when the given $predicate
callback
evaluates to true. Otherwise it will execute the $whenFalse
callback if any.
Unlike the ifThenElse
operation where the operation is applied to each element of the collection,
this operation operates on the collection directly.
Interface: Whenable
Signature: Collection::when(callable $predicate, callable $whenTrue, ?callable $whenFalse = null): Collection;
Collection::fromIterable([1, 2])
->when(
static fn() => true,
static fn(Iterator $collection): Collection => Collection::fromIterable($collection)->append(3)
); // [1, 2, 3]
window¶
Loop the collection yielding windows of data by adding a given number of items to the current item.
Initially the windows yielded will be smaller, until size 1 + $size
is reached.
Tip
To remove the window size constraint and have a dynamic window size, set the $size
to -1
.
Note
When $size
is equal to 0
, the window will only contain the current element, wrapped in an array.
Interface: Windowable
Signature: Collection::window(int $size): Collection;
<?php
declare(strict_types=1);
namespace App;
use loophp\collection\Collection;
include __DIR__ . '/../../../../vendor/autoload.php';
$data = range('a', 'e');
Collection::fromIterable($data)
->window(0); // [['a'], ['b'], ['c'], ['d'], ['e']]
Collection::fromIterable($data)
->window(1); // [['a'], ['a', 'b'], ['b', 'c'], ['c', 'd'], ['d', 'e']]
Collection::fromIterable($data)
->window(2); // [['a'], ['a', 'b'], ['a', 'b', 'c'], ['b', 'c', 'd'], ['c', 'd', 'e']]
Collection::fromIterable($data)
->window(-1); // [['a'], ['a', 'b'], ['a', 'b', 'c'], ['a', 'b', 'c', 'd'], ['a', 'b', 'c', 'd', 'e']]
words¶
Get a list of words from a string, splitting based on the character set: \t, \n, ' '
.
Interface: Wordsable
Signature: Collection::words(): Collection;
$string = <<<'EOF'
The quick brown fox jumps over the lazy dog.
This is another sentence.
EOF;
Collection::fromString($string)
->words();
// ['The', 'quick', 'brown', 'fox', 'jumps', 'over', 'the', 'lazy', 'dog.', 'This', 'is', 'another', 'sentence.']
wrap¶
Wrap every element into an array.
Interface: Wrapable
Signature: Collection::wrap(): Collection;
$associative = Collection::fromIterable(['a' => 'A', 'b' => 'B', 'c' => 'C'])
->wrap(); // [['a' => 'A'], ['b' => 'B'], ['c' => 'C']]
$list = Collection::fromIterable(range('a', 'c'))
->wrap(); // [[0 => 'a'], [1 => 'b'], [2 => 'c']]
zip¶
Zip a collection together with one or more iterables.
Interface: Zipable
Signature: Collection::zip(iterable ...$iterables): Collection;
$even = Collection::range(0, INF, 2);
$odd = Collection::range(1, INF, 2);
$positiveIntegers = Collection::fromIterable($even)
->zip($odd)
->limit(100)
->unwrap(); // [0, 1, 2, 3 ... 196, 197, 198, 199]