Skip to content

Commit

Permalink
added Iterables::memoize()
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Jun 18, 2024
1 parent 6310f42 commit 47f0b5a
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 0 deletions.
43 changes: 43 additions & 0 deletions src/Utils/Iterables.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,49 @@ public static function map(iterable $iterable, callable $transformer): \Generato
}


/**
* Wraps around iterator and caches its keys and values during iteration.
* This allows the data to be re-iterated multiple times.
* @template K
* @template V
* @param iterable<K, V> $iterable
* @return \IteratorAggregate<K, V>
*/
public static function memoize(iterable $iterable): iterable
{
return new class (self::toIterator($iterable)) implements \IteratorAggregate {
public function __construct(
private iterable $iterable,
private array $cache = [],
) {
}


public function getIterator(): \Generator
{
if (!$this->cache) {
$this->iterable->rewind();
}
$i = 0;
while (true) {
if (isset($this->cache[$i])) {
[$k, $v] = $this->cache[$i];
} elseif ($this->iterable->valid()) {
$k = $this->iterable->key();
$v = $this->iterable->current();
$this->iterable->next();
$this->cache[$i] = [$k, $v];
} else {
break;
}
yield $k => $v;
$i++;
}
}
};
}


/**
* Creates an iterator from anything that is iterable.
* @template K
Expand Down
77 changes: 77 additions & 0 deletions tests/Utils/Iterables.memoize().phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

/**
* Test: Nette\Utils\Iterables::memoize()
*/

declare(strict_types=1);

use Nette\Utils\Iterables;
use Tester\Assert;

require __DIR__ . '/../bootstrap.php';


function iterator(): Generator
{
yield 'a' => 'apple';
yield ['b'] => ['banana'];
yield 'c' => 'cherry';
}


test('iteration', function () {
$iterator = Iterables::memoize(iterator());

$pairs = [];
foreach ($iterator as $key => $value) {
$pairs[] = [$key, $value];
}
Assert::same(
[
['a', 'apple'],
[['b'], ['banana']],
['c', 'cherry'],
],
$pairs,
);
});


test('re-iteration', function () {
$iterator = Iterables::memoize(iterator());

foreach ($iterator as $value);

$pairs = [];
foreach ($iterator as $key => $value) {
$pairs[] = [$key, $value];
}
Assert::same(
[
['a', 'apple'],
[['b'], ['banana']],
['c', 'cherry'],
],
$pairs,
);
});


test('nested re-iteration', function () {
$iterator = Iterables::memoize(iterator());

$pairs = [];
foreach ($iterator as $key => $value) {
$pairs[] = [$key, $value];
foreach ($iterator as $value);
}
Assert::same(
[
['a', 'apple'],
[['b'], ['banana']],
['c', 'cherry'],
],
$pairs,
);
});

0 comments on commit 47f0b5a

Please sign in to comment.