1. Go to this page and download the library: Download dakujem/toru library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
// no extra memory wasted on creating a filtered array
$filtered = Itera::filter(input: $hugeDataSet, predicate: $filterFunction);
foreach($filtered as $key => $value){ /* ... */ }
use Dakujem\Toru\Itera;
// No memory wasted on creating a compound array. Especially true when the arrays are huge.
$all = Itera::chain($collection1, $collection2, [12,3,5], $otherCollection);
foreach ($all as $key => $element) {
// do not repeat yourself
}
$files = _dash(
new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir))
) // recursively iterate over a dir
->filter(fn(\SplFileInfo $fileInfo) => !$fileInfo->isDir()) // reject directories
->reindex(fn(\SplFileInfo $fileInfo) => $fileInfo->getPathname()); // index by full file path
use Dakujem\Toru\Pipeline;
use Dakujem\Toru\IteraFn;
$processed = Pipeline::through(
$collection,
IteraFn::filter(predicate: $filterFunction),
IteraFn::apply(values: $mapperFunction),
IteraFn::chain($moreElements),
IteraFn::valuesOnly(),
);
foreach ($processed as $value) {
// The filtered and mapped values from $collection will appear here,
// followed by the elements present in $moreElements.
}
use Dakujem\Toru\Dash;
use Dakujem\Toru\Itera;
use Dakujem\Toru\IteraFn;
Itera::chain(iterable ...$input): iterable
// `append` is only present in `Dash` and `IteraFn` classes as an alias to `chain`
Dash::append(iterable ...$more): Dash
IteraFn::append(iterable ...$more): callable
use Dakujem\Toru\Itera;
Itera::unfold(
[1,2,3,4],
fn($value, $key) => ['double value for key ' . $key => $value * 2],
)
// or "produce new index and key based on the original value"
Itera::unfold(
['one:1', 'two:2', 'three:3'],
function(string $value) {
[$name, $index] = split(':', $value); // there could be a fairly complex regex here
return [$index => $name];
},
)
use Dakujem\Toru\Itera;
Itera::reduce(iterable $input, callable $reducer, mixed $initial): mixed
use Dakujem\Toru\Dash;
// The value is returned directly, because it is not iterable:
Dash::collect([1,2,3])->reduce(fn() => 42); // 42
// The value `[42]` is iterable, thus a new `Dash` instance is returned:
Dash::collect([1,2,3])->reduce(fn() => [42])->count(); // 1
use Dakujem\Toru\Itera;
Itera::filter(iterable $input, callable $predicate): iterable
fn(mixed $value, mixed $key): bool
> new CallbackFilterIterator(Itera::toIterator($input), $predicate)
>
use Dakujem\Toru\Itera;
Itera::limit(iterable $input, int $limit): iterable
Itera::omit(iterable $input, int $omit): iterable
Itera::slice(iterable $input, int $offset, int $limit): iterable
use Dakujem\Toru\Itera;
Itera::tap(iterable $input, callable $effect): iterable
Itera::each(iterable $input, callable $effect): iterable // alias for `tap`
fn(mixed $value, mixed $key): void
use Dakujem\Toru\Itera;
Itera::repeat(mixed $input): iterable
Itera::loop(iterable $input): iterable
Itera::replicate(iterable $input, int $times): iterable
use Dakujem\Toru\Itera;
Itera::produce(callable $producer): iterable
use Dakujem\Toru\Itera;
Itera::limit(Itera::produce(fn() => rand()), 1000); // a sequence of 1000 pseudorandom numbers
Itera::produce(fn() => 42); // an infinite sequence of the answer to life, the universe, and everything
Itera::produce(fn(int $i) => $i); // an infinite sequence of integers 0, 1, 2, 3, ...
use Dakujem\Toru\Dash;
use Dakujem\Toru\Itera;
// Create a generator from an input collection.
$collection = Itera::apply(input: Itera::filter(input: $input, filter: $filter), values: $mapper);
// or using Dash
$collection = Dash::collect($input)->filter(filter: $filter)->apply(values: $mapper);
// No generator code has been executed so far.
// The evaluation of $filter and $mapper callables begins with the first iteration below.
foreach($collection as $key => $value) {
// Only at this point the mapper and filter functions are executed,
// once per element of the input collection.
// The generator execution is then _paused_ until the next iteration.
}
use Dakujem\Toru\Dash;
use Dakujem\Toru\Itera;
$keySum = Dash::collect($input)
->filter( /* ... */ )
->aggregate(function (iterable $collection): int {
$keySum = 0;
foreach ($collection as $k => $v) {
$keySum += $k;
}
return $keySum;
}); // no more fluent calls, integer is returned
$median = Dash::collect($input)
->filter( /* ... */ )
->aggregate(function (iterable $collection): int {
return MyCalculus::computeMedian(Itera::toArray($collection));
}); // the median value is returned
use Dakujem\Toru\Dash;
use Dakujem\Toru\Itera;
class MyItera extends Itera
{
/**
* A shorthand for mapping arrays to use instead of `array_map`
* with keys provided for the mapper.
*/
public static function mapToArray(iterable $input, callable $mapper): iterable
{
return static::toArray(
static::apply(input: $input, values: $mapper),
);
}
public static function appendBar(iterable $input): iterable
{
return static::chain($input, ['bar' => 'bar']);
}
}
class MyDash extends Dash
{
public function appendFoo(): self
{
return new static(
Itera::chain($this->collection, ['foo' => 'foo'])
);
}
public function appendBar(): self
{
return new static(
MyItera::appendBar($this->collection)
);
}
public function aggregateZero(): int
{
return 0; // ¯\_(ツ)_/¯
}
}
// 0 (zero)
MyDash::collect([1, 2, 3])->appendFoo()->appendBar()->aggregateZero();
// [1, 2, 3, 'foo' => 'foo', 'bar' => 'bar']
MyDash::collect([1, 2, 3])->appendFoo()->appendBar()->toArray();
// [1, 2, 3, 'foo' => 'foo', 'bar' => 'bar'] (generator)
MyItera::appendBar([1, 2, 3]);
use Dakujem\Toru\Dash;
if (!function_exists('_dash')) {
function _dash(iterable $input): Dash {
return Dash::collect($input);
}
}
$generatorFunction = function(): iterable {
yield 1;
yield 2;
yield 'foo' => 'bar';
yield 42;
};
$generatorObject = $generatorFunction();
foreach ($generatorObject as $key => $val) { /* ... */ }
// subsequent iteration will throw an exception
// Exception: Cannot rewind a generator that was already run
foreach ($generatorObject as $key => $val) { /* ... */ }
// Instead of iterating over the same generator object, the generator function
// is called multiple times. Each call creates a new generator object.
foreach ($generatorFunction() as $key => $val) { /* ... */ }
// A new generator object is created with every call to $generatorFunction().
foreach ($generatorFunction() as $key => $val) { /* ... */ } // ✅ no exception
use Dakujem\Toru\Dash;
// Not possible, the generator function is not iterable itself
$it = Dash::collect($generatorFunction); // TypeError
// Not possible, the argument to `collect` must be iterable
$it = Dash::collect(fn() => $generatorFunction()); // TypeError
// The correct way is to wrap the generator returned by the call,
// but it has the same drawback as described above
$dash = Dash::collect($generatorFunction());
foreach ($dash->filter($filterFn) as $val) { /* ... */ }
// Exception: Cannot rewind a generator that was already run
foreach ($dash->filter($otherFilterFn) as $val) { /* ... */ } // fails
use Dakujem\Toru\Dash;
use Dakujem\Toru\Regenerator;
$dash = new Regenerator(fn() => Dash::collect($generatorFunction()));
foreach ($dash->filter($filterFn) as $val) { /* ... */ }
foreach ($dash->filter($otherFilterFn) as $val) { /* ... */ } // works, hooray!
use Dakujem\Toru\Itera;
$filtered = Itera::filter($input, $predicate);
$mapped = Itera::apply($filtered, $mapper);
foreach($mapped as $k => $v) { /* ...*/ }
// Exception: Cannot rewind a generator that was already run
foreach($filtered as $k => $v) { /* ...*/ }
use Dakujem\Toru\Itera;
$filtered = fn() => Itera::filter($input, $predicate);
$mapped = fn() => Itera::apply($filtered(), $mapper);
foreach($mapped() as $k => $v) { /* ...*/ }
// this will work, the issue is mitigated by iterating over a new generator
foreach($filtered() as $k => $v) { /* ...*/ }
use Dakujem\Toru\Itera;
use Dakujem\Toru\Regenerator;
$filtered = new Regenerator(fn() => Itera::filter($input, $predicate));
$mapped = new Regenerator(fn() => Itera::apply($filtered(), $mapper));
foreach($mapped as $k => $v) { /* ...*/ }
// In this case, the regenerator handles function calls.
foreach($filtered as $k => $v) { /* ...*/ }
use Dakujem\Toru\IteraFn;
use Dakujem\Toru\Pipeline;
$alteredCollection = Pipeline::through(
$collection,
IteraFn::filter(predicate: $filterFunction),
IteraFn::map(values: $mapper),
);
// Pipelines are not limited to producing iterable collections, they may produce any value types:
$average = Pipeline::through(
$collection,
IteraFn::filter(predicate: $filterFunction),
IteraFn::reduce(reducer: fn($carry, $current) => $carry + $current, initial: 0),
fn(int $sum) => $sum / $sizeOfCollection,
);
use Dakujem\Toru\Itera;
use Dakujem\Toru\IteraFn;
use Dakujem\Toru\Pipeline;
use Dakujem\Toru\Dash;
$sequence = Itera::produce(fn() => rand()); // infinite iterator
// A naive foreach may be the best solution in certain cases...
$count = 0;
$array = [];
foreach ($sequence as $i) {
if (0 == $i % 2) {
$array[$i] = 'the value is ' . $i;
if ($count >= 1000) {
break;
}
}
}
// While the standalone static methods may be handy,
// they are not suited for complex computations.
$interim = Itera::filter($sequence, fn($i) => 0 == $i % 2);
$interim = Itera::reindex($interim, fn($i) => $i);
$interim = Itera::apply($interim, fn($i) => 'the value is ' . $i);
$interim = Itera::limit($interim, 1000);
$array = Itera::toArray($interim);
// Without the interim variable(s), the reading order of the calls is reversed
// and the whole computation is not exactly legible.
$array = Itera::toArray(
Itera::limit(
Itera::apply(
Itera::reindex(
Itera::filter(
$sequence,
fn($i) => 0 == $i % 2,
),
fn($i) => $i,
),
fn($i) => 'the value is ' . $i,
),
1000,
)
);
// Complex pipelines may be composed using partially applied callables.
$array = Pipeline::through(
$sequence,
IteraFn::filter(fn($i) => 0 == $i % 2),
IteraFn::reindex(fn($i) => $i),
IteraFn::apply(fn($i) => 'the value is ' . $i),
IteraFn::limit(1000),
IteraFn::toArray(),
);
// Lodash-style fluent call chaining.
$array = Dash::collect($sequence)
->filter(fn($i) => 0 == $i % 2)
->reindex(fn($i) => $i)
->map(fn($i) => 'the value is ' . $i)
->limit(1000)
->toArray();
$images = [];
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
/** @var SplFileInfo $fileInfo */
foreach ($iterator as $fileInfo) {
// Skip directories
if ($fileInfo->isDir()) {
continue;
}
// Get the full path of the file
$filePath = $fileInfo->getPathname();
// Reject non-image files (hacky)
if (!@getimagesize($filePath)) {
continue;
}
$images[$filePath] = $fileInfo;
}
$listImages = function(string $dir): Generator {
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
/** @var SplFileInfo $fileInfo */
foreach ($iterator as $fileInfo) {
// Skip directories
if ($fileInfo->isDir()) {
continue;
}
// Get the full path of the file
$filePath = $fileInfo->getPathname();
// Reject non-image files (hacky)
if (!@getimagesize($fileInfo->getPathname())) {
continue;
}
yield $filePath => $fileInfo;
}
};
$images = $listImages($dir);
$images = _dash(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir))) // recursively iterate over a dir
->filter(fn(SplFileInfo $fileInfo) => !$fileInfo->isDir()) // reject directories
->filter(fn(SplFileInfo $fileInfo) => @getimagesize($fileInfo->getPathname())) // accept only images (hacky)
->reindex(fn(SplFileInfo $fileInfo) => $fileInfo->getPathname()); // key by the full file path