Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
name: Tests

on: [push, pull_request]
on: [ pull_request ]

jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
os: [ubuntu-latest, windows-latest]
php: [7.4, 8.0]
dependency-version: [prefer-lowest, prefer-stable]
os: [ ubuntu-latest, windows-latest ]
php: [ 7.4, 8.0 ]

name: P${{ matrix.php }} - ${{ matrix.dependency-version }} - ${{ matrix.os }}
name: PHP ${{ matrix.php }} - ${{ matrix.os }}

steps:
- name: Checkout code
Expand All @@ -24,8 +23,16 @@ jobs:
php-version: ${{ matrix.php }}
coverage: none

- name: Cache composer dependencies
id: cache-run
uses: actions/cache@v2
with:
path: vendor
key: composer-${{ matrix.php }}-${{ matrix.os }}-${{ hashFiles('composer.lock') }}

- name: Install dependencies
run: composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction
if: steps.cache-run.outputs.cache-hit != 'true'
run: composer update --prefer-dist --no-interaction

- name: Execute tests
run: vendor/bin/pest
run: vendor/bin/pest
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Changed
- Added `.gitattributes` to reduce disk usage.
- Reimplemented using `IteratorAggregate` to improve performance.

## [1.0.1] - 2021-08-29
### Changed
Expand Down
5 changes: 5 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
"src/iterator_functions.php"
]
},
"autoload-dev": {
"psr-4": {
"DoekeNorg\\IteratorFunctions\\Tests\\": "tests"
}
},
"license": "MIT",
"authors": [
{
Expand Down
76 changes: 27 additions & 49 deletions src/Iterator/ColumnIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/**
* Iterator that returns a single column for the iteration array / object.
*/
class ColumnIterator extends \FilterIterator
class ColumnIterator implements \IteratorAggregate
{
/**
* The column to return.
Expand All @@ -25,6 +25,12 @@ class ColumnIterator extends \FilterIterator
*/
private int $count = 0;

/**
* The inner iterator.
* @var \Traversable
*/
private \Traversable $iterator;

/**
* Creates the iterator.
* @param \Traversable $iterator The iterator that provides the arrays / objects.
Expand All @@ -33,76 +39,48 @@ class ColumnIterator extends \FilterIterator
*/
public function __construct(\Traversable $iterator, $column_key, $index_key = null)
{
parent::__construct($iterator);

$this->iterator = $iterator;
$this->column_key = $column_key;
$this->index_key = $index_key;
}

/**
* @inheritdoc
*/
public function next(): void
{
parent::next();

if (isset($this->index_key) && !$this->column($this->index_key)) {
++$this->count;
}
}

/**
* @inheritdoc
*/
public function key()
{
if (isset($this->index_key)) {
return $this->column($this->index_key) ?? $this->count;
}

return parent::key();
}

/**
* @inheritdoc
*/
public function current()
{
if (isset($this->column_key)) {
return $this->column($this->column_key);
}

return parent::current();
}

/**
* Retrieves a single column from the current iteration.
* @param array|\ArrayAccess|object $iteration The iteration.
* @param int|string $key The key to retrieve from the iteration.
* @return mixed|null The value.
* @return mixed The value.
*/
protected function column($key)
private function getColumn($iteration, $key)
{
$iteration = parent::current();
if (is_array($iteration) || $iteration instanceof \ArrayAccess) {
return $iteration[$key] ?? null;
}

if (is_object($iteration)) {
return $iteration->{$key} ?? null;
return $iteration->{$key} ?: null;
}

return null;
}

/**
* @inheritdoc
* @inheritDoc
*/
public function accept(): bool
public function getIterator(): \Generator
{
if (isset($this->column_key)) {
return $this->column($this->column_key) !== null;
foreach ($this->iterator as $key => $iteration) {
if (isset($this->index_key)) {
$key = $this->getColumn($iteration, $this->index_key) ?? $this->count++;
}

if (isset($this->column_key)) {
$iteration = $this->getColumn($iteration, $this->column_key);
if ($iteration === null) {
continue;
}
}

yield $key => $iteration;
}

return true;
}
}
23 changes: 16 additions & 7 deletions src/Iterator/FlipIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,30 @@
/**
* Iterator that flips the key and the value of the current iteration.
*/
class FlipIterator extends \IteratorIterator
class FlipIterator implements \IteratorAggregate
{
/**
* @inheritdoc
* The inner iterator.
* @var \Traversable
*/
public function key()
private \Traversable $iterator;

/**
* Creates the iterator.
* @param \Traversable $iterator The inner iterator.
*/
public function __construct(\Traversable $iterator)
{
return parent::current();
$this->iterator = $iterator;
}

/**
* @inheritdoc
* @inheritDoc
*/
public function current()
public function getIterator(): \Generator
{
return parent::key();
foreach ($this->iterator as $key => $value) {
yield $value => $key;
}
}
}
26 changes: 21 additions & 5 deletions src/Iterator/KeysIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,30 @@
/**
* Iterator that returns only the keys of the provided iterator.
*/
class KeysIterator extends ValuesIterator
class KeysIterator implements \IteratorAggregate
{
/**
* @inheritdoc
* @psalm-suppress UndefinedInterfaceMethod Psalm doesn't understand the inner iterator is an {@see \Iterator}.
* The inner iterator.
* @var \Traversable
*/
public function current()
private \Traversable $iterator;

/**
* Creates the iterator.
* @param \Traversable $iterator The inner iterator.
*/
public function __construct(\Traversable $iterator)
{
$this->iterator = $iterator;
}

/**
* @inheritDoc
*/
public function getIterator(): \Generator
{
return $this->getInnerIterator()->key();
foreach ($this->iterator as $key => $_) {
yield $key;
}
}
}
34 changes: 15 additions & 19 deletions src/Iterator/MapIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@
/**
* Iterator that applies a callback to the elements of the given iterators.
*/
class MapIterator extends \IteratorIterator
class MapIterator implements \IteratorAggregate
{
/**
* Callback function to run for each element in each iterator.
* @var callable
*/
private $callback;

/**
* The inner iterator.
* @var \MultipleIterator
*/
private \MultipleIterator $iterator;

/**
* Returns the iterator.
* @param array|\Iterator ...$iterators Any iterator to apply the callback on. Can also be an array.
Expand All @@ -23,41 +29,31 @@ public function __construct(callable $callback, iterable ...$iterators)
try {
$function = new \ReflectionFunction(\Closure::fromCallable($callback));
} catch (\ReflectionException $e) {
throw new \RuntimeException($e->getMessage(), (int) $e->getCode(), $e);
throw new \RuntimeException($e->getMessage(), (int)$e->getCode(), $e);
}

if (!$function->isVariadic() && $function->getNumberOfParameters() !== count($iterators)) {
throw new \InvalidArgumentException('The callback needs as many arguments as provided iterators.');
}

$inner = new \MultipleIterator();
$this->iterator = new \MultipleIterator();
foreach ($iterators as $iterator) {
if (is_array($iterator)) {
$iterator = new \ArrayIterator($iterator);
}
$inner->attachIterator($iterator);
$this->iterator->attachIterator($iterator);
}

parent::__construct($inner);

$this->callback = $callback;
}

/**
* @inheritdoc
* @inheritDoc
*/
public function key()
public function getIterator(): \Generator
{
$keys = parent::key();

return is_array($keys) ? reset($keys) : parent::key();
}

/**
* @inheritdoc
*/
public function current()
{
return call_user_func_array($this->callback, parent::current());
foreach ($this->iterator as $key => $value) {
yield $key[0] => ($this->callback)(...$value);
}
}
}
40 changes: 13 additions & 27 deletions src/Iterator/ValuesIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,30 @@
/**
* Iterator that returns only the values of the provided iterator.
*/
class ValuesIterator extends \IteratorIterator
class ValuesIterator implements \IteratorAggregate
{
/**
* The internal key count.
* @var int
* The inner iterator.
* @var \Traversable
*/
private int $count = 0;
private \Traversable $iterator;

/**
* @inheritdoc
* Creates the iterator.
* @param \Traversable $iterator The inner iterator.
*/
public function key(): int
public function __construct(\Traversable $iterator)
{
return $this->count;
$this->iterator = $iterator;
}

/**
* @inheritdoc
*
* Increases the internal count.
*
* @inheritDoc
*/
public function next(): void
public function getIterator(): \Generator
{
parent::next();
++$this->count;
}

/**
* @inheritdoc
*
* Resets the internal count.
*
*/
public function rewind(): void
{
parent::rewind();

$this->count = 0;
foreach ($this->iterator as $value) {
yield $value;
}
}
}