diff --git a/README.md b/README.md index 9114fb5..9b6a844 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,7 @@ All entry points always return an instance of the pipeline. | `fold()` | Reduces input values to a single value. Defaults to summation. Requires an initial value. | `array_reduce`, `Aggregate`, `Sum` | | `reduce()` | Alias to `fold()` with a reversed order of arguments. | `array_reduce` | | `flip()` | Swaps keys and values. | `array_flip` | +| `tuples()` | Converts stream to [key, value] tuples. | | | `max()` | Finds the highest value. | `max` | | `min()` | Finds the lowest value. | `min` | | `count()` | Counts values. Eagerly executed.| `array_count` | diff --git a/src/Standard.php b/src/Standard.php index 69e15b2..cf485ca 100644 --- a/src/Standard.php +++ b/src/Standard.php @@ -47,6 +47,7 @@ use function min; use function mt_getrandmax; use function mt_rand; +use function array_keys; /** * Concrete pipeline with sensible default callbacks. @@ -1053,6 +1054,39 @@ private static function flipKeysAndValues(iterable $previous): iterable } } + /** + * @return $this + */ + public function tuples() + { + if ($this->empty()) { + // No-op: null. + return $this; + } + + if (is_array($this->pipeline)) { + $this->pipeline = array_map( + fn($key, $value) => [$key, $value], + array_keys($this->pipeline), + $this->pipeline + ); + + return $this; + } + + + $this->pipeline = self::toTuples($this->pipeline); + + return $this; + } + + private static function toTuples(iterable $previous): iterable + { + foreach ($previous as $key => $value) { + yield [$key, $value]; + } + } + private function feedRunningVariance(Helper\RunningVariance $variance, ?callable $castFunc): self { if (null === $castFunc) { diff --git a/tests/TuplesTest.php b/tests/TuplesTest.php new file mode 100644 index 0000000..41be828 --- /dev/null +++ b/tests/TuplesTest.php @@ -0,0 +1,109 @@ + + * + * 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 ArrayIterator; +use PHPUnit\Framework\TestCase; + +use Pipeline\Standard; +use IteratorIterator; + +use function Pipeline\fromArray; +use function round; +use function sqrt; +use function Pipeline\take; +use function Pipeline\map; + +/** + * @covers \Pipeline\Standard + * + * @internal + */ +final class TuplesTest extends TestCase +{ + public static function provideArrays(): iterable + { + yield 'empty_array' => [[], []]; + yield 'basic_key_values' => [['a' => 1, 'b' => 2, 'c' => 3], [['a', 1], ['b', 2], ['c', 3]]]; + yield 'numeric_keys' => [[10, 20, 30], [[0, 10], [1, 20], [2, 30]]]; + + yield 'mixed_keys_values' => [ + ['name' => 'Alice', 1 => 'Bob'], + [['name', 'Alice'], [1, 'Bob']], + ]; + + yield 'null_values' => [['x' => null, 'y' => null], [['x', null], ['y', null]]]; + + yield 'empty_string_key' => [['' => 'value'], [['', 'value']]]; + + yield 'nested_arrays' => [['outer' => ['inner1' => 'value1', 'inner2' => 'value2']], [['outer', ['inner1' => 'value1', 'inner2' => 'value2']]]]; + } + + public static function provideIterables(): iterable + { + foreach (self::provideArrays() as $name => $item) { + yield $item; + + $iteratorItem = $item; + $iteratorItem[0] = new ArrayIterator($iteratorItem[0]); + + yield "$name(ArrayIterator)" => $iteratorItem; + + $iteratorItem = $item; + $iteratorItem[0] = new IteratorIterator(new ArrayIterator($iteratorItem[0])); + + yield "$name(IteratorIterator)" => $iteratorItem; + + $iteratorItem = $item; + $iteratorItem[0] = fromArray($iteratorItem[0]); + + yield "$name(Pipeline)" => $iteratorItem; + } + + yield 'generator' => [map(function () { + yield '1' => 2; + yield '2' => 3; + }), [['1', 2], ['2', 3]]]; + } + + /** + * @dataProvider provideIterables + */ + public function testTuples(iterable $input, array $expected, bool $preserve_keys = false): void + { + $pipeline = take($input); + + $pipeline->tuples(); + + $this->assertSame( + $expected, + $pipeline->toArray($preserve_keys) + ); + } + + public function testNoop(): void + { + $pipeline = new Standard(); + + $pipeline->tuples(); + + $this->assertSame([], $pipeline->toArray()); + } +}