From 330bb4fa49d92a27097f8ac5b5162f6f51fd84b6 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 6 Jul 2021 18:08:44 +0200 Subject: [PATCH] Update Filter operation. BREAKING CHANGE: yes --- docs/pages/api.rst | 22 +++++------- docs/pages/code/operations/filter.php | 23 ++++++++++++ src/Operation/Filter.php | 52 +++++++++++++++++++++------ 3 files changed, 74 insertions(+), 23 deletions(-) create mode 100644 docs/pages/code/operations/filter.php diff --git a/docs/pages/api.rst b/docs/pages/api.rst index 43b189d71..386dddf3b 100644 --- a/docs/pages/api.rst +++ b/docs/pages/api.rst @@ -717,22 +717,18 @@ Signature: ``Collection::falsy();`` filter ~~~~~~ -Filter collection items based on one or more callbacks. Multiple callbacks will be treated as a logical ``AND``. +Filter collection items based on one or more callbacks. + +.. warning:: The `callbacks` parameter is variadic and will be evaluated as a logical ``OR``. + If you're looking for a logical ``AND``, you have to make multiple calls to the + same operation. Interface: `Filterable`_ Signature: ``Collection::filter(callable ...$callbacks);`` -.. code-block:: php - - $divisibleBy3 = static fn($value): bool => 0 === $value % 3; - $divisibleBy6 = static fn($value): bool => 0 === $value % 6; - - $collection = Collection::fromIterable(range(1, 10)) - ->filter($divisibleBy3); // [3, 6, 9] - - $collection = Collection::fromIterable(range(1, 10)) - ->filter($divisibleBy3, $divisibleBy6); // [6] +.. literalinclude:: code/operations/filter.php + :language: php first ~~~~~ @@ -773,13 +769,13 @@ Signature: ``Collection::flatMap(callable $callback);`` .. code-block:: php - $square = static fn (int $val): int => $val ** 2; + $square = static fn (int $val): int => $val ** 2; $squareArray = static fn (int $val): array => [$val ** 2]; $squareCollection = static fn (int $val): Collection => Collection::fromIterable([$val ** 2]); $collection = Collection::fromIterable(range(1, 3)) ->flatMap($squareArray); // [1, 4, 9] - + $collection = Collection::fromIterable(range(1, 3)) ->flatMap($squareCollection); // [1, 4, 9] diff --git a/docs/pages/code/operations/filter.php b/docs/pages/code/operations/filter.php new file mode 100644 index 000000000..672b78e98 --- /dev/null +++ b/docs/pages/code/operations/filter.php @@ -0,0 +1,23 @@ + 0 === $value % 3; +$divisibleBy5 = static fn($value): bool => 0 === $value % 5; + +$collection = Collection::fromIterable(range(1, 10)) + ->filter($divisibleBy3); // [3, 6, 9] + +$collection = Collection::fromIterable(range(1, 10)) + ->filter($divisibleBy3, $divisibleBy5); // [3, 5, 6, 0, 10] diff --git a/src/Operation/Filter.php b/src/Operation/Filter.php index 478130969..a66fc9796 100644 --- a/src/Operation/Filter.php +++ b/src/Operation/Filter.php @@ -9,8 +9,8 @@ namespace loophp\collection\Operation; -use CallbackFilterIterator; use Closure; +use Generator; use Iterator; /** @@ -26,7 +26,7 @@ final class Filter extends AbstractOperation /** * @pure * - * @return Closure(callable(T , TKey, Iterator): bool ...): Closure (Iterator): Iterator + * @return Closure(callable(T , TKey, Iterator): bool ...): Closure (Iterator): Generator */ public function __invoke(): Closure { @@ -34,15 +34,41 @@ public function __invoke(): Closure /** * @param callable(T, TKey, Iterator): bool ...$callbacks * - * @return Closure(Iterator): Iterator + * @return Closure(Iterator): Generator */ static fn (callable ...$callbacks): Closure => /** * @param Iterator $iterator * - * @return Iterator + * @return Generator */ - static function (Iterator $iterator) use ($callbacks): Iterator { + static function (Iterator $iterator) use ($callbacks): Generator { + // TODO: Find a way to avoid repeating this everywhere. + $reducerCallback = + /** + * @param TKey $key + * + * @return Closure(T): Closure(Iterator): Closure(bool, callable(T, TKey, Iterator): bool): bool + */ + static fn ($key): Closure => + /** + * @param T $current + * + * @return Closure(Iterator): Closure(bool, callable(T, TKey, Iterator): bool): bool + */ + static fn ($current): Closure => + /** + * @param Iterator $iterator + * + * @return Closure(bool, callable(T, TKey, Iterator): bool): bool + */ + static fn (Iterator $iterator): Closure => + /** + * @param bool $carry + * @param callable(T, TKey, Iterator): bool $callable + */ + static fn (bool $carry, callable $callable): bool => $carry || $callable($current, $key, $iterator); + $defaultCallback = /** * @param T $value @@ -53,11 +79,17 @@ static function (Iterator $iterator) use ($callbacks): Iterator { [$defaultCallback] : $callbacks; - return array_reduce( - $callbacks, - static fn (Iterator $carry, callable $callback): CallbackFilterIterator => new CallbackFilterIterator($carry, $callback), - $iterator - ); + foreach ($iterator as $key => $current) { + $result = array_reduce( + $callbacks, + $reducerCallback($key)($current)($iterator), + false + ); + + if ($result) { + yield $key => $current; + } + } }; } }