From 6e5c758e5f8cc465359737840bf0a5797421965f Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Tue, 20 Apr 2021 17:17:14 +0200 Subject: [PATCH] feat: Add Coalesce operation. --- docs/pages/api.rst | 31 +++++++++++- docs/pages/code/operations/coalesce.php | 19 ++++++++ spec/loophp/collection/CollectionSpec.php | 19 ++++++++ .../collection/Operation/CoalesceSpec.php | 48 +++++++++++++++++++ src/Collection.php | 6 +++ src/Contract/Collection.php | 3 ++ src/Contract/Operation/Coalesceable.php | 22 +++++++++ src/Operation/Coalesce.php | 31 ++++++++++++ 8 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 docs/pages/code/operations/coalesce.php create mode 100644 spec/loophp/collection/Operation/CoalesceSpec.php create mode 100644 src/Contract/Operation/Coalesceable.php create mode 100644 src/Operation/Coalesce.php diff --git a/docs/pages/api.rst b/docs/pages/api.rst index 6f96d594e..272c08a5b 100644 --- a/docs/pages/api.rst +++ b/docs/pages/api.rst @@ -309,6 +309,26 @@ Signature: ``Collection::chunk(int $size);`` $collection->chunk(2); +coalesce +~~~~~~~~ + +Return the first *non-nullsy* value in a collection. + +*Nullsy* values are: + +* The null value: null +* Empty array: [] +* The integer zero: 0 +* The boolean: false +* The empty string: '' + +Interface: `Coalesceable`_ + +Signature: ``Collection::coalesce();`` + +.. literalinclude:: code/operations/coalesce.php + :language: php + collapse ~~~~~~~~ @@ -1179,7 +1199,15 @@ Signature: ``Collection::nth(int $step, int $offset = 0);`` nullsy ~~~~~~ -Check if the collection contains nullsy values. +Check if the collection contains *nullsy* values. + +*Nullsy* values are: + +* The null value: `null` +* Empty array: `[]` +* The integer zero: `0` +* The boolean: `false` +* The empty string: `''` Interface: `Nullsyable`_ @@ -2143,6 +2171,7 @@ Signature: ``Collection::zip(iterable ...$iterables);`` .. _Combinateable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Combinateable.php .. _Combineable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Combineable.php .. _Compactable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Compactable.php +.. _Coalesceable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Coalesceable.php .. _Containsable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Containsable.php .. _Currentable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Currentable.php .. _Cycleable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Cycleable.php diff --git a/docs/pages/code/operations/coalesce.php b/docs/pages/code/operations/coalesce.php new file mode 100644 index 000000000..5ec306f5d --- /dev/null +++ b/docs/pages/code/operations/coalesce.php @@ -0,0 +1,19 @@ +coalesce(); // [ 0 => 'a' ] + +$input = ['', null, 'foo', false, ...range('a', 'e')]; + +$collection = Collection::fromIterable($input) + ->coalesce(); // [ 2 => 'foo' ] diff --git a/spec/loophp/collection/CollectionSpec.php b/spec/loophp/collection/CollectionSpec.php index 61a3c6ed0..4097cf1c7 100644 --- a/spec/loophp/collection/CollectionSpec.php +++ b/spec/loophp/collection/CollectionSpec.php @@ -476,6 +476,25 @@ public function it_can_chunk(): void ->shouldIterateAs([[0 => 'A', 1 => 'B'], [0 => 'C', 1 => 'D', 2 => 'E'], [0 => 'F']]); } + public function it_can_coalesce(): void + { + $input = range('a', 'e'); + + $this::fromIterable($input) + ->coalesce() + ->shouldIterateAs([ + 0 => 'a', + ]); + + $input = ['', null, 'foo', false, ...range('a', 'e')]; + + $this::fromIterable($input) + ->coalesce() + ->shouldIterateAs([ + 2 => 'foo', + ]); + } + public function it_can_collapse(): void { $generator = static function () { diff --git a/spec/loophp/collection/Operation/CoalesceSpec.php b/spec/loophp/collection/Operation/CoalesceSpec.php new file mode 100644 index 000000000..a93ff73e5 --- /dev/null +++ b/spec/loophp/collection/Operation/CoalesceSpec.php @@ -0,0 +1,48 @@ +__invoke()($iterator) + ->shouldHaveCount(1); + + $this + ->__invoke()($iterator) + ->shouldIterateAs([ + 0 => 'a', + ]); + + $input = ['', null, 'foo', false, ...range('a', 'e')]; + + $iterator = new ArrayIterator($input); + + $this + ->__invoke()($iterator) + ->shouldHaveCount(1); + + $this + ->__invoke()($iterator) + ->shouldIterateAs([ + 2 => 'foo', + ]); + } + + public function it_is_initializable() + { + $this->shouldHaveType(Coalesce::class); + } +} diff --git a/src/Collection.php b/src/Collection.php index 00388608e..197afe9f2 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -18,6 +18,7 @@ use loophp\collection\Operation\AsyncMap; use loophp\collection\Operation\Cache; use loophp\collection\Operation\Chunk; +use loophp\collection\Operation\Coalesce; use loophp\collection\Operation\Collapse; use loophp\collection\Operation\Column; use loophp\collection\Operation\Combinate; @@ -201,6 +202,11 @@ public function chunk(int ...$sizes): CollectionInterface return new self(Chunk::of()(...$sizes), $this->getIterator()); } + public function coalesce(): CollectionInterface + { + return new self(Coalesce::of(), $this->getIterator()); + } + public function collapse(): CollectionInterface { return new self(Collapse::of(), $this->getIterator()); diff --git a/src/Contract/Collection.php b/src/Contract/Collection.php index 39202e929..bb47cb222 100644 --- a/src/Contract/Collection.php +++ b/src/Contract/Collection.php @@ -15,6 +15,7 @@ use loophp\collection\Contract\Operation\AsyncMapable; use loophp\collection\Contract\Operation\Cacheable; use loophp\collection\Contract\Operation\Chunkable; +use loophp\collection\Contract\Operation\Coalesceable; use loophp\collection\Contract\Operation\Collapseable; use loophp\collection\Contract\Operation\Columnable; use loophp\collection\Contract\Operation\Combinateable; @@ -123,6 +124,7 @@ * @template-extends AsyncMapable * @template-extends Cacheable * @template-extends Chunkable + * @template-extends Coalesceable * @template-extends Collapseable * @template-extends Columnable * @template-extends Combinateable @@ -224,6 +226,7 @@ interface Collection extends AsyncMapable, Cacheable, Chunkable, + Coalesceable, Collapseable, Columnable, Combinateable, diff --git a/src/Contract/Operation/Coalesceable.php b/src/Contract/Operation/Coalesceable.php new file mode 100644 index 000000000..e5767e78a --- /dev/null +++ b/src/Contract/Operation/Coalesceable.php @@ -0,0 +1,22 @@ + + */ + public function coalesce(): Collection; +} diff --git a/src/Operation/Coalesce.php b/src/Operation/Coalesce.php new file mode 100644 index 000000000..7809962a6 --- /dev/null +++ b/src/Operation/Coalesce.php @@ -0,0 +1,31 @@ +): Generator + */ + public function __invoke(): Closure + { + /** @psalm-var Closure(Iterator): Generator $pipe */ + $pipe = Pipe::of()( + Compact::of()(), + Head::of(), + ); + + // Point free style. + return $pipe; + } +}