jumpifbelow / php-functional-array
Classes to make the use of sets functional
Installs: 4 390
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Forks: 0
pkg:composer/jumpifbelow/php-functional-array
Requires
- php: >=8.1
Requires (Dev)
- phpunit/phpunit: ^12.2.7
- symfony/var-dumper: *
- dev-master
- 15.0.0
- 14.x-dev
- 14.0.0
- 13.x-dev
- 13.0.1
- 13.0.0
- 12.1.0
- 12.0.0
- 11.1.0
- 11.0.3
- 11.0.2
- 11.0.1
- 11.0.0
- 10.1.0
- 10.0.0
- 9.0.0
- 8.1.1
- 8.1.0
- 8.0.0
- 7.2.0
- 7.1.0
- 7.0.1
- 7.0.0
- 6.0.0
- 5.0.0
- 4.3.2
- 4.3.1
- 4.3.0
- 4.2.0
- 4.1.1
- 4.1.0
- 4.0.0
- 3.0.3
- 3.0.2
- 3.0.1
- 3.0.0
- 2.1.0
- 2.0.1
- 2.0.0
- 1.1.0
- 1.0.0
- dev-29-rename-foreachoperator-for-tapoperator
This package is auto-updated.
Last update: 2025-10-15 09:41:44 UTC
README
Development stage
The project is in a fairly advanced development phase. The API should be stable from now, but it is still possible that huge changes would be made. As small as it could be, any breaking change will come in major release, whatever it is.
General note
This package comes with 3 interfaces and 3 classes implementing them. Purpose of each interfaces:
FluentArrayInterface: allowing to use native PHP function in an object-oriented way.ExtendedArrayInterface: likeFluentArrayInterfacebut with custom operations implemented. It should be the one you want to use if you seek for more functionalities. If you do prefer to ensure the native performance of PHP, avoiding that class would ensure you that the called methods are using native PHP functions.FluentIteratorInterface: inspired byFluentArrayInterface, but for virtually infinite set. It has less functions as many would not make sense to provide, but it handles big set without exhausting memory. It will only execute altering methods once iterating through it, allowing to handle exactly where your code will be executed, rather than on-call. It would handle a lot better any stream or resource providing data.
How to install it?
Just run Composer:
composer require jumpifbelow/php-functional-array
How to use it?
FluentArray
Two types of array are made.
The first, JumpIfBelow\Arrays\FluentArrayInterface is
made to work like a PHP array, with all functions builtin directly in methods.
All of the calls are immutable and fluent, meaning you can chain it without
altering the first reference. The only exceptions is when managing internal pointer,
where it won't send a new object as it would break the iterator.
The second, JumpIfBelow\Arrays\ExtendedArrayInterface is made to be like JumpIfBelow\Arrays\FluentArrayInterface.
However, as it lacks some common methods, it adds them to be easier to handle.
Of course, the package comes with implementations you can already use like this:
<?php
use JumpIfBelow\Arrays\FluentArray;
$a = FluentArray::from([
5,
'er',
'ert',
'loop',
false,
null,
]);
$newArray = $a
->filter(function ($x): bool {
return is_string($x);
})
->map(function (string $x): string {
return strrev($x);
})
->sort()
;
var_dump($newArray->toArray());
// those examples could be shortened because they are using defined functions
// note that the interface accepts any callable, which allow this writing
$newArray = $a
->filter('is_string')
->map('strrev')
->sort()
;
// you can quickly setup an array and directly operate with it too
$pairSquareSum = FluentArray
::from([1, 4, 6, 5, 9, 8, 0])
->filter(fn(int $x) => $x % 2 === 0)
->map(fn(int $x) => $x ** 2)
->reduce(fn(int $sum, int $x) => $sum + $x, 0)
;
FluentIterable
Then, there is the iterable object FluentIterable.
It is made with the idea to handle infinite sets without exhausting all of the memory and ending up with an error. Therefore, the execution relies a lot more on the CPU, the rest depending on your code. As it will be executed on run-time, the items will go through operators one by one, instead of generating getting each step completed before going to another one. This is benefical as you will receive items one by one as they are processed, rather than the whole big result block. You could imagine reading and parsing a very large file, while only acting on certain line, without having to load the whole file in the memory, but only the parts you are interested in.
<?php
use JumpIfBelow\Arrays\FluentIterable;
// we are creating a sum of a column in a CSV file
$sum = FluentIterable
::from(function () {
$f = fopen('myfile.csv', 'r');
while (($line = fgetcsv($f)) !== false) {
yield $line;
}
fclose($f);
})
->map(fn (array $row) => $row[1])
->filter(fn (int $value) => $value >= 0)
->reduce(fn (int $sum, int $value) => $sum + $value, 0)
;
// all of the code, from file opening and operations will only be executed when reduce is started
// once reduce is done, it will close the file handler
Note that transforming a FluentIterable to another one is an operation handled by an OperatorInterface.
So while using the map operation, you only call in fact the MapOperator internally.
The APIs are public to allow using your very own operators if needed.
If we retake the above example, we could write it this way:
<?php
use JumpIfBelow\Arrays\FluentIterable;
use JumpIfBelow\Arrays\IterableOperator\{
FilterOperator,
MapOperator,
};
$sum = FluentIterable
::from(function () {
$f = fopen('myfile.csv', 'r');
while (($line = fgetcsv($f)) !== false) {
yield $line;
}
fclose($f);
})
->apply(
MapOperator::with(fn (array $row) => $row[1]),
FilterOperator::with(fn (int $value) => $value >= 0),
)
->reduce(fn (int $sum, int $value) => $sum + $value, 0)
;
It is also powerful enough not to generate everything at once.
For example, this would exhaust the memory right from the range call:
<?php
use JumpIfBelow\Arrays\FluentArray;
$array = FluentArray
::range(1, 2 ** 32 - 1)
->filter(fn (int $v): bool => $v >= 10 ** 7 && $v < 10 ** 8)
->slice(5, 10)
;
Meanwhile, you can go with a far greater set, filtering it, getting only a part of it, without actually going in the whole set. In this example, we would go from 0 to 2 ** 64 (if you are on a 64-bits OS). Then, only filtering values between 10,000,000 and 100,000,000. Afterwards, getting the values starting after the 5th one, and keeping only 10 of them. The whole set will not be generated all at once, and will not go further than 10,000,014.
<?php
use JumpIfBelow\Arrays\FluentIterable;
$iterable = FluentIterable
::range(0, PHP_INT_MAX)
->filter(fn (int $v): bool => $v >= 10 ** 7 && $v < 10 ** 8)
->slice(5, 10)
;
Immutable and mutable methods
This is supposed to only use immutable reference, meaning calling any function that edit the content should return a new version of the array with the altered data. The old reference remains unmodified. There are exceptions to this behavior because of the way some functions are behaving. Here is a list of methods:
FluentArray
Mutable
popshift
Immutable
arsortasortchangeKeyCasechunkcolumncombinecountValuesdiffdiffAssocdiffKeydiffUassocdiffUkeyfillKeysfilterflipintersectintersectAssocintersectKeyintersectUassocintersectUkeykeyskrsortksortmapmergemergeRecursivemultisortnatcasesortnatsortpadpushrandreplacereplaceRecursivereversersortshuffleslicesortspliceuasortudiffudiffAssocudiffUassocuintersectuintersectAssocuintersectUassocuksortuniqueunshiftusortvalueswalkwalkRecursive
ExtendedArray
Mutable
nextKeynextValuenextEntry
Immutable
forEacheverysomeentryfindKeyfindValuequantilesquantileKeysquantileValuesflatgroupByindexByfetchswapwithfullMapfullReducepermutationfirstlast
FluentIterable
Mutable
None
Immutable
applyforEachmapfilterreducereplacesliceuniqueindexBysomeeverytoArray