Skip to content

Commit

Permalink
feat: Add UniqueIterableAggregate.
Browse files Browse the repository at this point in the history
  • Loading branch information
drupol committed Jun 14, 2022
1 parent 3a55e45 commit eef38bc
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 0 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ The missing PHP iterators.
* `SimpleCachingIteratorAggregate`
* `StringIteratorAggregate`
* `TypedIteratorAggregate`
* `UniqueIterableAggregate`
* `UnpackIterableAggregate`

## Installation
Expand Down Expand Up @@ -137,6 +138,22 @@ $value will yield the following values:
*/
```

### UniqueIterableAggregate

```php
<?php

$generator = static function(): Generator {
while (true) {
yield mt_rand(0, 9);
}
};

$iterator = new UniqueIterableAggregate($generator(), 1000);

foreach ($iterator as $value) {} // 9 random values only.
```

### UnpackIterableAggregate

```php
Expand Down
68 changes: 68 additions & 0 deletions src/UniqueIterableAggregate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

declare(strict_types=1);

namespace loophp\iterators;

use Generator;
use IteratorAggregate;
use Traversable;

use function in_array;

use const PHP_INT_MAX;

/**
* @template TKey
* @template T
*
* @implements IteratorAggregate<TKey, T>
*/
final class UniqueIterableAggregate implements IteratorAggregate
{
/**
* @var iterable<TKey, T>
*/
private iterable $iterable;

private int $retries;

/**
* @param iterable<TKey, T> $iterable
*/
public function __construct(iterable $iterable, int $retries = PHP_INT_MAX)
{
$this->iterable = $iterable;
$this->retries = $retries;
}

/**
* @return Generator<TKey, T>
*/
public function getIterator(): Traversable
{
$retries = $this->retries;
$seen = [];

foreach ($this->iterable as $key => $value) {
if (0 === $retries) {
break;
}

if (in_array($value, $seen, true)) {
--$retries;

continue;
}

$seen[] = $value;

yield $key => $value;
}
}
}
34 changes: 34 additions & 0 deletions tests/unit/UniqueIterableAggregatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

declare(strict_types=1);

namespace tests\loophp\iterators;

use loophp\iterators\MersenneTwisterRNGIteratorAggregate;
use loophp\iterators\UniqueIterableAggregate;
use PHPUnit\Framework\TestCase;

/**
* @internal
* @coversDefaultClass \loophp\iterators
*/
final class UniqueIterableAggregatorTest extends TestCase
{
public function testBasic(): void
{
$input = (new MersenneTwisterRNGIteratorAggregate())
->withMin(0)
->withMax(9)
->withSeed(123);

$iterator = new UniqueIterableAggregate($input, 1000);

$expected = [2, 9, 3 => 0, 6 => 6, 7 => 7, 9 => 4, 10 => 1, 18 => 3, 22 => 5, 37 => 8];
self::assertSame($expected, iterator_to_array($iterator));
}
}

0 comments on commit eef38bc

Please sign in to comment.