From 48d9620ad4ddf29f2f4d697bd659019452abb21d Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Sun, 4 Oct 2020 06:52:53 +0200 Subject: [PATCH] feat: Add Span operation. --- docs/pages/api.rst | 17 ++++++++ spec/loophp/collection/CollectionSpec.php | 22 +++++++++++ src/Collection.php | 6 +++ src/Contract/Collection.php | 3 ++ src/Contract/Operation/Spanable.php | 20 ++++++++++ src/Operation/Span.php | 48 +++++++++++++++++++++++ 6 files changed, 116 insertions(+) create mode 100644 src/Contract/Operation/Spanable.php create mode 100644 src/Operation/Span.php diff --git a/docs/pages/api.rst b/docs/pages/api.rst index 1a66ef79d..479b7a209 100644 --- a/docs/pages/api.rst +++ b/docs/pages/api.rst @@ -1530,6 +1530,22 @@ Signature: ``Collection::sort(?callable $callback = null);`` ->sort() // Sort the values (which are now the keys) ->flip(); // Flip again to put back the keys and values, sorted by keys. +span +~~~~ + +Returns a tuple where first element is longest prefix (possibly empty) of elements that satisfy the callback and second element is the remainder. + +Interface: `Spanable`_ + +Signature: ``Collection::span(callable $callback);`` + +.. code-block:: php + + $input = range(1, 10); + + Collection::fromIterable($input) + ->span(fn ($x) => $x < 4); // [ [1, 2, 3], [4, 5, 6, 7, 8, 9, 10] ] + split ~~~~~ @@ -1974,6 +1990,7 @@ Signature: ``Collection::zip(iterable ...$iterables);`` .. _Sinceable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Sinceable.php .. _Sliceable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Sliceable.php .. _Sortable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Sortable.php +.. _Spanable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Spanable.php .. _Splitable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Splitable.php .. _Tailable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Tailable.php .. _Tailsable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Tailsable.php diff --git a/spec/loophp/collection/CollectionSpec.php b/spec/loophp/collection/CollectionSpec.php index b739ad474..63eca6422 100644 --- a/spec/loophp/collection/CollectionSpec.php +++ b/spec/loophp/collection/CollectionSpec.php @@ -1986,6 +1986,28 @@ static function ($left, $right): int { ->shouldIterateAs($output()); } + public function it_can_span(): void + { + $input = range(1, 10); + + $test = $this::fromIterable($input) + ->span(static function (int $x): bool {return 4 > $x; }); + + $test + ->first() + ->current() + ->shouldIterateAs( + [1, 2, 3] + ); + + $test + ->last() + ->current() + ->shouldIterateAs( + [3 => 4, 4 => 5, 5 => 6, 6 => 7, 7 => 8, 8 => 9, 9 => 10] + ); + } + public function it_can_split(): void { $splitter = static function ($value): bool { diff --git a/src/Collection.php b/src/Collection.php index 2f315f051..b0f843ea6 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -88,6 +88,7 @@ use loophp\collection\Operation\Since; use loophp\collection\Operation\Slice; use loophp\collection\Operation\Sort; +use loophp\collection\Operation\Span; use loophp\collection\Operation\Split; use loophp\collection\Operation\Tail; use loophp\collection\Operation\Tails; @@ -746,6 +747,11 @@ public function sort(int $type = Operation\Sortable::BY_VALUES, ?callable $callb return $this->run(Sort::of()($type)($callback)); } + public function span(callable $callback): CollectionInterface + { + return $this->run(Span::of()($callback)); + } + public function split(int $type = Operation\Splitable::BEFORE, callable ...$callbacks): CollectionInterface { return $this->run(Split::of()($type)(...$callbacks)); diff --git a/src/Contract/Collection.php b/src/Contract/Collection.php index e30f4b60b..d7c362c9d 100644 --- a/src/Contract/Collection.php +++ b/src/Contract/Collection.php @@ -81,6 +81,7 @@ use loophp\collection\Contract\Operation\Sinceable; use loophp\collection\Contract\Operation\Sliceable; use loophp\collection\Contract\Operation\Sortable; +use loophp\collection\Contract\Operation\Spanable; use loophp\collection\Contract\Operation\Splitable; use loophp\collection\Contract\Operation\Tailable; use loophp\collection\Contract\Operation\Tailsable; @@ -178,6 +179,7 @@ * @template-extends Sinceable * @template-extends Sliceable * @template-extends Sortable + * @template-extends Spanable * @template-extends Splitable * @template-extends Tailable * @template-extends Tailsable @@ -275,6 +277,7 @@ interface Collection extends Sinceable, Sliceable, Sortable, + Spanable, Splitable, Tailable, Tailsable, diff --git a/src/Contract/Operation/Spanable.php b/src/Contract/Operation/Spanable.php new file mode 100644 index 000000000..1f0d615f3 --- /dev/null +++ b/src/Contract/Operation/Spanable.php @@ -0,0 +1,20 @@ + + */ + public function span(callable $callback): Collection; +} diff --git a/src/Operation/Span.php b/src/Operation/Span.php new file mode 100644 index 000000000..1f66c32c3 --- /dev/null +++ b/src/Operation/Span.php @@ -0,0 +1,48 @@ +): bool)): Closure(Iterator): Generator> + */ + public function __invoke(): Closure + { + return + /** + * @psalm-param callable(T, TKey, Iterator):bool $callback + * + * @psalm-return Closure(Iterator): Generator> + */ + static function (callable $callback): Closure { + return + /** + * @psalm-param Iterator $iterator + * + * @psalm-return Generator> + */ + static function (Iterator $iterator) use ($callback): Generator { + /** @psalm-var Iterator $takeWhile */ + $takeWhile = TakeWhile::of()($callback)($iterator); + /** @psalm-var Iterator $dropWhile */ + $dropWhile = DropWhile::of()($callback)($iterator); + + return yield from [$takeWhile, $dropWhile]; + }; + }; + } +}