Skip to content

Commit

Permalink
Add each() (#135)
Browse files Browse the repository at this point in the history
  • Loading branch information
sanmai committed Oct 8, 2023
1 parent 1cc5976 commit c48f45c
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 0 deletions.
13 changes: 13 additions & 0 deletions README.md
Expand Up @@ -126,6 +126,7 @@ All entry points always return an instance of the pipeline.
| `max()` | Finds the highest value. | `max` |
| `min()` | Finds the lowest value. | `min` |
| `count()` | Counts values. Eagerly executed.| `array_count` |
| `each()` | Eagerly iterates over the sequence. | `foreach`, `array_walk` |
| `runningCount()` | Counts seen values using a reference argument. | |
| `toArray()` | Returns an array with all values. Eagerly executed. | `dict`, `ToDictionary` |
| `toArrayPreservingKeys()` | Returns an array with all values and keys. Eagerly executed. | |
Expand Down Expand Up @@ -387,6 +388,18 @@ $result = $pipeline->toArray();

If in the example about one would use `iterator_to_array($result)` they would get just `[3, 4]`.

## `$pipeline->each()`

Eagerly iterates over the sequence using the provided callback.

```php
$pipeline->each(function ($i) {
$this->log("Saw $i");
});
```

Discards the sequence after iteration unless instructed otherwise by the second argument.

## `$pipeline->getIterator()`

A method to conform to the `Traversable` interface. In case of unprimed `\Pipeline\Standard` it'll return an empty array iterator, essentially a no-op pipeline. Therefore this should work without errors:
Expand Down
23 changes: 23 additions & 0 deletions src/Standard.php
Expand Up @@ -1121,4 +1121,27 @@ public function finalVariance(

return $variance;
}

/**
* Eagerly iterates over the sequence using the provided callback. Discards the sequence after iteration.
*
* @param callable $func
* @param bool $discard Whenever to discard the pipeline's interator.
*/
public function each(callable $func, bool $discard = true): void
{
if ($this->empty()) {
return;
}

try {
foreach ($this->pipeline as $key => $value) {
$func($value, $key);
}
} finally {
if ($discard) {
$this->discard();
}
}
}
}
154 changes: 154 additions & 0 deletions tests/EachTest.php
@@ -0,0 +1,154 @@
<?php
/**
* Copyright 2017, 2018 Alexey Kopytko <alexey@kopytko.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

declare(strict_types=1);

namespace Tests\Pipeline;

use LogicException;
use PHPUnit\Framework\TestCase;
use Pipeline\Standard;

use ArrayIterator;

use function Pipeline\map;
use function Pipeline\take;
use function Pipeline\fromArray;

/**
* @covers \Pipeline\Standard
*
* @internal
*/
final class EachTest extends TestCase
{
private array $output;

protected function setUp(): void
{
parent::setUp();
$this->output = [];
}

protected function observeValue($value): void
{
$this->output[] = $value;
}

protected function observeKeyValue($key, $value): void
{
$this->output[$key] = $value;
}

public function testUninitialized(): void
{
$pipeline = new Standard();

$pipeline->each(fn ($value) => $this->observeValue($value));

$this->assertSame([], $this->output);
}

public function testEmpty(): void
{
$pipeline = take([]);

$pipeline->each(fn ($value) => $this->observeValue($value));

$this->assertSame([], $this->output);
}

public function testEmptyGenerator(): void
{
$pipeline = map(static fn () => yield from []);

$pipeline->each(fn ($value) => $this->observeValue($value));

$this->assertSame([], $this->output);
}

public function testNotEmpty(): void
{
$pipeline = take([1, 2, 3, 4]);

$pipeline->each(fn (int $value) => $this->observeValue($value));

$this->assertSame([1, 2, 3, 4], $this->output);
}

public function testKeyValue(): void
{
$pipeline = take([5 => 1, 2, 3, 4]);

$pipeline->each(fn (int $value, int $key) => $this->observeKeyValue($key, $value));

$this->assertSame([5 => 1, 2, 3, 4], $this->output);
}


public static function provideInterrupt(): iterable
{
yield [map(fn () => yield from [1, 2, 3, 4])];
yield [take(new ArrayIterator([1, 2, 3, 4]))];
}

/**
* @dataProvider provideInterrupt
*/
public function testInterrupt(Standard $pipeline): void
{
$pipeline->cast(function (int $value) {
if (3 === $value) {
throw new LogicException();
}

return $value;
});

try {
$pipeline->each(function (int $value): void {
$this->observeValue($value);
});
} catch (LogicException $_) {
$this->assertSame([1, 2], $this->output);
}

$pipeline->each(function (int $value): void {
$this->fail();
});

$this->assertSame([1, 2], $this->output);
$this->assertSame([], $pipeline->toArray());
}

public function testNoDiscard(): void
{
$pipeline = fromArray([1, 2, 3]);

$pipeline->each(function (int $value): void {
$this->observeValue($value);
}, false);

$pipeline->each(function (int $value): void {
$this->observeValue($value);
});

$this->assertSame([1, 2, 3, 1, 2, 3], $this->output);
$this->assertSame([], $pipeline->toArray());
}

}

0 comments on commit c48f45c

Please sign in to comment.