Skip to content

Commit

Permalink
Add RandomIterator Iterator.
Browse files Browse the repository at this point in the history
  • Loading branch information
drupol committed Sep 2, 2020
1 parent 192e7ad commit ce64bca
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 11 deletions.
29 changes: 29 additions & 0 deletions spec/loophp/collection/Iterator/RandomIteratorSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace spec\loophp\collection\Iterator;

use ArrayIterator;
use loophp\collection\Iterator\RandomIterator;
use PhpSpec\ObjectBehavior;

class RandomIteratorSpec extends ObjectBehavior
{
public function it_can_get_the_innerIterator()
{
$this->getInnerIterator()->shouldBeAnInstanceOf(ArrayIterator::class);
}

public function it_is_initializable()
{
$this->shouldHaveType(RandomIterator::class);
}

public function let()
{
$iterator = new ArrayIterator(range(0, 4));

$this->beConstructedWith($iterator);
}
}
107 changes: 107 additions & 0 deletions src/Iterator/RandomIterator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

declare(strict_types=1);

namespace loophp\collection\Iterator;

use ArrayIterator;
use Iterator;
use OuterIterator;

/**
* @psalm-template TKey
* @psalm-template TKey of array-key
* @psalm-template T of string
*
* @implements Iterator<TKey, T>
*/
final class RandomIterator implements Iterator, OuterIterator
{
/**
* @var array<int, int>
*/
private $indexes;

/**
* @var Iterator
* @psalm-var Iterator<TKey, T>
*/
private $inner;

/**
* @var ArrayIterator
* @psalm-var ArrayIterator<int, array{0: TKey, 1: T}>
*/
private $iterator;

/**
* @var int
*/
private $key;

/**
* @psalm-param Iterator<TKey, T> $iterator
*/
public function __construct(Iterator $iterator)
{
$this->inner = $iterator;
$this->iterator = $this->buildArrayIterator($iterator);
$this->indexes = array_keys($this->iterator->getArrayCopy());
$this->key = array_rand($this->indexes);
}

public function current()
{
$value = $this->iterator[$this->key];

return $value[1];
}

public function getInnerIterator()
{
return $this->inner;
}

public function key()
{
$value = $this->iterator[$this->key];

return $value[0];
}

public function next(): void
{
unset($this->indexes[$this->key]);

if ($this->valid()) {
$this->key = array_rand($this->indexes);
}
}

public function rewind()
{
$this->iterator->rewind();
}

public function valid(): bool
{
return [] !== $this->indexes;
}

/**
* @psalm-param Iterator<TKey, T> $iterator
*
* @psalm-return ArrayIterator<int, array{0: TKey, 1: T}>
*/
private function buildArrayIterator(Iterator $iterator): ArrayIterator
{
/** @psalm-var ArrayIterator<int, array{0: TKey, 1: T}> $arrayIterator */
$arrayIterator = new ArrayIterator();

foreach ($iterator as $key => $value) {
$arrayIterator->append([$key, $value]);
}

return $arrayIterator;
}
}
13 changes: 2 additions & 11 deletions src/Operation/Shuffle.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Closure;
use Generator;
use Iterator;
use loophp\collection\Iterator\RandomIterator;

/**
* @psalm-template TKey
Expand All @@ -24,17 +25,7 @@ public function __invoke(): Closure
* @psalm-return Generator<TKey, T, mixed, void>
*/
static function (Iterator $iterator): Generator {
/** @psalm-var Iterator<int, array{0: TKey, 1: T}> $pack */
$pack = Pack::of()($iterator);
$data = iterator_to_array($pack);

while ([] !== $data) {
$randomKey = array_rand($data);

yield $data[$randomKey][0] => $data[$randomKey][1];

unset($data[$randomKey]);
}
return yield from new RandomIterator($iterator);
};
}
}

0 comments on commit ce64bca

Please sign in to comment.