diff --git a/README.md b/README.md index d2d17ac..bae3f02 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ p\array_map(fn ($x) => $x * 2); - `p\iterable_any(?callable $callback = null)` — returns `true` if any item matches (or is `=== true` when callback is `null`); short-circuits - `p\iterable_filter(callable $callback)` — yields matching items for which `$callback` returns `true` - `p\iterable_first(iterable $iterable)` — returns first item or `null` (**consumes one element**) +- `p\iterable_flatten(callable $callable)` — lazily flattens the an iterable of iterables - `p\iterable_map(callable $callback)` — yields items mapped over `$callback`. Preserves keys. - `p\iterable_nth(int $n)` — returns the nth item (0-based); consumes up to n+1 items; returns `null` if out of range - `p\iterable_reduce(callable $callback, $initial = null)` — reduces an iterable to a single value diff --git a/composer.json b/composer.json index fea0bb2..f9e3462 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,12 @@ "autoload": { "files": [ "src/pipe.php" - ] + ] + }, + "autoload-dev": { + "psr-4": { + "Anarchitecture\\pipe\\Tests\\": "tests/" + } }, "require-dev": { "phpstan/phpstan": "^2.1", diff --git a/src/pipe.php b/src/pipe.php index 68e5ffe..cf9a5a8 100644 --- a/src/pipe.php +++ b/src/pipe.php @@ -561,6 +561,28 @@ function iterable_first(iterable $iterable): mixed { return null; } +/** + * Returns a unary callable that lazily flattens it's arguments. + * + * @param bool $preserve_keys + * + * @return callable(iterable>) : Generator + */ +function iterable_flatten(bool $preserve_keys = true): callable { + return function (iterable $iterable) use ($preserve_keys) : Generator { + /** @var iterable $value */ + foreach ($iterable as $value) { + if ($preserve_keys) { + yield from $value; + } else { + foreach ($value as $inner) { + yield $inner; + } + } + } + }; +} + /** * Return unary callable for mapping over an iterable * @@ -771,7 +793,7 @@ function iterable_zip(iterable ...$right) : \Closure { return new \IteratorIterator($iterable); }; - return static function(iterable $left) use ($right, $mapper) : \Generator { + return static function(iterable $left) use ($right, $mapper) : Generator { $right = \array_map($mapper, $right); diff --git a/tests/IterableFlattenTest.php b/tests/IterableFlattenTest.php new file mode 100644 index 0000000..6fe4f52 --- /dev/null +++ b/tests/IterableFlattenTest.php @@ -0,0 +1,58 @@ + iterable_map(fn($i) => $i < 5 ? [1,2,3] : self::fail('this function should not be invoked this many times')) + |> iterable_flatten() + |> iterable_take($take_amount); + + // this should run without failing + self::assertSame(count(iterator_to_array($result, preserve_keys: false)), $take_amount); + } +}