Skip to content

Commit c48f45c

Browse files
authored
Add each() (#135)
1 parent 1cc5976 commit c48f45c

File tree

3 files changed

+190
-0
lines changed

3 files changed

+190
-0
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ All entry points always return an instance of the pipeline.
126126
| `max()` | Finds the highest value. | `max` |
127127
| `min()` | Finds the lowest value. | `min` |
128128
| `count()` | Counts values. Eagerly executed.| `array_count` |
129+
| `each()` | Eagerly iterates over the sequence. | `foreach`, `array_walk` |
129130
| `runningCount()` | Counts seen values using a reference argument. | |
130131
| `toArray()` | Returns an array with all values. Eagerly executed. | `dict`, `ToDictionary` |
131132
| `toArrayPreservingKeys()` | Returns an array with all values and keys. Eagerly executed. | |
@@ -387,6 +388,18 @@ $result = $pipeline->toArray();
387388

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

391+
## `$pipeline->each()`
392+
393+
Eagerly iterates over the sequence using the provided callback.
394+
395+
```php
396+
$pipeline->each(function ($i) {
397+
$this->log("Saw $i");
398+
});
399+
```
400+
401+
Discards the sequence after iteration unless instructed otherwise by the second argument.
402+
390403
## `$pipeline->getIterator()`
391404

392405
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:

src/Standard.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,4 +1121,27 @@ public function finalVariance(
11211121

11221122
return $variance;
11231123
}
1124+
1125+
/**
1126+
* Eagerly iterates over the sequence using the provided callback. Discards the sequence after iteration.
1127+
*
1128+
* @param callable $func
1129+
* @param bool $discard Whenever to discard the pipeline's interator.
1130+
*/
1131+
public function each(callable $func, bool $discard = true): void
1132+
{
1133+
if ($this->empty()) {
1134+
return;
1135+
}
1136+
1137+
try {
1138+
foreach ($this->pipeline as $key => $value) {
1139+
$func($value, $key);
1140+
}
1141+
} finally {
1142+
if ($discard) {
1143+
$this->discard();
1144+
}
1145+
}
1146+
}
11241147
}

tests/EachTest.php

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
<?php
2+
/**
3+
* Copyright 2017, 2018 Alexey Kopytko <[email protected]>
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
declare(strict_types=1);
19+
20+
namespace Tests\Pipeline;
21+
22+
use LogicException;
23+
use PHPUnit\Framework\TestCase;
24+
use Pipeline\Standard;
25+
26+
use ArrayIterator;
27+
28+
use function Pipeline\map;
29+
use function Pipeline\take;
30+
use function Pipeline\fromArray;
31+
32+
/**
33+
* @covers \Pipeline\Standard
34+
*
35+
* @internal
36+
*/
37+
final class EachTest extends TestCase
38+
{
39+
private array $output;
40+
41+
protected function setUp(): void
42+
{
43+
parent::setUp();
44+
$this->output = [];
45+
}
46+
47+
protected function observeValue($value): void
48+
{
49+
$this->output[] = $value;
50+
}
51+
52+
protected function observeKeyValue($key, $value): void
53+
{
54+
$this->output[$key] = $value;
55+
}
56+
57+
public function testUninitialized(): void
58+
{
59+
$pipeline = new Standard();
60+
61+
$pipeline->each(fn ($value) => $this->observeValue($value));
62+
63+
$this->assertSame([], $this->output);
64+
}
65+
66+
public function testEmpty(): void
67+
{
68+
$pipeline = take([]);
69+
70+
$pipeline->each(fn ($value) => $this->observeValue($value));
71+
72+
$this->assertSame([], $this->output);
73+
}
74+
75+
public function testEmptyGenerator(): void
76+
{
77+
$pipeline = map(static fn () => yield from []);
78+
79+
$pipeline->each(fn ($value) => $this->observeValue($value));
80+
81+
$this->assertSame([], $this->output);
82+
}
83+
84+
public function testNotEmpty(): void
85+
{
86+
$pipeline = take([1, 2, 3, 4]);
87+
88+
$pipeline->each(fn (int $value) => $this->observeValue($value));
89+
90+
$this->assertSame([1, 2, 3, 4], $this->output);
91+
}
92+
93+
public function testKeyValue(): void
94+
{
95+
$pipeline = take([5 => 1, 2, 3, 4]);
96+
97+
$pipeline->each(fn (int $value, int $key) => $this->observeKeyValue($key, $value));
98+
99+
$this->assertSame([5 => 1, 2, 3, 4], $this->output);
100+
}
101+
102+
103+
public static function provideInterrupt(): iterable
104+
{
105+
yield [map(fn () => yield from [1, 2, 3, 4])];
106+
yield [take(new ArrayIterator([1, 2, 3, 4]))];
107+
}
108+
109+
/**
110+
* @dataProvider provideInterrupt
111+
*/
112+
public function testInterrupt(Standard $pipeline): void
113+
{
114+
$pipeline->cast(function (int $value) {
115+
if (3 === $value) {
116+
throw new LogicException();
117+
}
118+
119+
return $value;
120+
});
121+
122+
try {
123+
$pipeline->each(function (int $value): void {
124+
$this->observeValue($value);
125+
});
126+
} catch (LogicException $_) {
127+
$this->assertSame([1, 2], $this->output);
128+
}
129+
130+
$pipeline->each(function (int $value): void {
131+
$this->fail();
132+
});
133+
134+
$this->assertSame([1, 2], $this->output);
135+
$this->assertSame([], $pipeline->toArray());
136+
}
137+
138+
public function testNoDiscard(): void
139+
{
140+
$pipeline = fromArray([1, 2, 3]);
141+
142+
$pipeline->each(function (int $value): void {
143+
$this->observeValue($value);
144+
}, false);
145+
146+
$pipeline->each(function (int $value): void {
147+
$this->observeValue($value);
148+
});
149+
150+
$this->assertSame([1, 2, 3, 1, 2, 3], $this->output);
151+
$this->assertSame([], $pipeline->toArray());
152+
}
153+
154+
}

0 commit comments

Comments
 (0)