From 0e5c45c8046298212347a0bfb659126af8e75d2e Mon Sep 17 00:00:00 2001 From: Alexey Kopytko Date: Sat, 29 Apr 2023 20:21:51 +0900 Subject: [PATCH] Add runningCount (#127) --- README.md | 2 + src/Standard.php | 23 ++++++++++ tests/RunningCountTest.php | 92 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 tests/RunningCountTest.php diff --git a/README.md b/README.md index eb9e32b..4178bd3 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,8 @@ All entry points always return an instance of the pipeline. | `flip()` | Swaps keys and values. | `array_flip` | | `max()` | Finds the highest value. | `max` | | `min()` | Finds the lowest value. | `min` | +| `count()` | Counts values. Eagerly executed.| `array_count` | +| `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. | | | `runningVariance()` | Computes online statistics: sample mean, sample variance, standard deviation. | [Welford's method](https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm) | diff --git a/src/Standard.php b/src/Standard.php index d9fda55..ccc3e36 100644 --- a/src/Standard.php +++ b/src/Standard.php @@ -547,6 +547,29 @@ public function toArrayPreservingKeys(): array return $this->toArray(true); } + /** + * Counts seen values online. + * + * @param ?int &$count the current count; initialized unless provided + * + * @param-out int $count + * + * @return $this + */ + public function runningCount( + ?int &$count + ): self { + $count ??= 0; + + $this->cast(static function ($input) use (&$count) { + ++$count; + + return $input; + }); + + return $this; + } + /** * {@inheritdoc} * diff --git a/tests/RunningCountTest.php b/tests/RunningCountTest.php new file mode 100644 index 0000000..2f4fbda --- /dev/null +++ b/tests/RunningCountTest.php @@ -0,0 +1,92 @@ + + * + * 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 PHPUnit\Framework\TestCase; +use function Pipeline\map; +use function Pipeline\take; +use function range; + +/** + * @covers \Pipeline\Standard + * + * @internal + */ +final class RunningCountTest extends TestCase +{ + public function testRunningCount(): void + { + $countEven = 1; + + $pipeline = map(fn () => yield from range(0, 100)) + ->runningCount($countAll) + ->filter(fn (int $n) => 0 === $n % 2) + ->runningCount($countEven) + ->filter(fn (int $n) => $n % 3); + + $this->assertSame(0, $countAll); + $this->assertSame(1, $countEven); + + $this->assertSame(34, $pipeline->count()); + + $this->assertSame(101, $countAll); + $this->assertSame(51, $countEven - 1); + } + + public function testRunningCountLazy(): void + { + $countEven = 1; + + $pipeline = map(fn () => yield from range(0, 100)) + ->runningCount($countAll) + ->filter(fn (int $n) => 0 === $n % 2) + ->runningCount($countEven) + ->filter(fn (int $n) => $n % 3); + + $this->assertSame(0, $countAll); + $this->assertSame(1, $countEven); + + foreach ($pipeline as $item) { + $this->assertSame(2, $item); + + break; + } + + // Because we need to inspect 3 numbers to get to 2: filtered out 0 and 1 + $this->assertSame(3, $countAll); + $this->assertSame(3, $countEven); + } + + public function testRunningCountArray(): void + { + $countEven = 1; + + $pipeline = take(range(0, 100)) + ->runningCount($countAll) + ->filter(fn (int $n) => 0 === $n % 2) + ->runningCount($countEven) + ->filter(fn (int $n) => $n % 3); + + $this->assertSame(101, $countAll); + $this->assertSame(51, $countEven - 1); + + $this->assertSame(34, $pipeline->count()); + } +}