Skip to content

Commit

Permalink
refactor: Update Has operation.
Browse files Browse the repository at this point in the history
  • Loading branch information
drupol committed Jan 8, 2021
1 parent e646c8c commit 88cae79
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 31 deletions.
28 changes: 16 additions & 12 deletions docs/pages/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -823,29 +823,33 @@ Signature: ``Collection::groupBy(?callable $callback = null);``
has
~~~

Check if the collection has a value.
Check if the collection has values.

.. warning:: The `callbacks` parameter is variadic and they are evaluated as a logical ``OR``.
If you're looking for a logical ``AND``, you have make multiple calls to the
since operations.

Interface: `Hasable`_

Signature: ``Collection::has(callable $callback);``
Signature: ``Collection::has(callable ...$callbacks);``

.. code-block:: php
Collection::fromIterable(range('A', 'C'))
->has(
static function ($value, $key, Iterator $iterator) {
return 'A';
}
)
->current(); // true
static fn ($value, $key, Iterator $iterator): string => 'A'
); // [true]
Collection::fromIterable(range('A', 'C'))
->has(
static function ($value, $key, Iterator $iterator) {
return 'D';
}
)
->current(); // false
static fn ($value, $key, Iterator $iterator): string => 'D'
); // [false]
Collection::fromIterable(range('A', 'C'))
->has(
static fn ($value, $key, Iterator $iterator): string => 'A',
static fn ($value, $key, Iterator $iterator): string => 'Z'
); // [true]
head
~~~~
Expand Down
26 changes: 22 additions & 4 deletions spec/loophp/collection/CollectionSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -1261,31 +1261,35 @@ public function it_can_groupBy(): void

public function it_can_has(): void
{
$this::fromIterable(range('A', 'C'))
$input = range('A', 'C');

$this::fromIterable($input)
->has(
static function ($value, $key) {
return 'A';
}
)
->shouldIterateAs([true]);

$this::fromIterable(range('A', 'C'))
$this::fromIterable($input)
->has(
static function ($value, $key) {
return 'Z';
}
)
->shouldIterateAs([false]);

$this::fromIterable(['b', 1, 'foo', 'bar'])
$input = ['b', 1, 'foo', 'bar'];

$this::fromIterable($input)
->has(
static function ($value, $key) {
return 'foo';
}
)
->shouldIterateAs([2 => true]);

$this::fromIterable(['b', 1, 'foo', 'bar'])
$this::fromIterable($input)
->has(
static function ($value, $key) {
return 'unknown';
Expand All @@ -1300,6 +1304,20 @@ static function ($value, $key) {
}
)
->shouldIterateAs([false]);

$this::fromIterable($input)
->has(
static fn () => 1,
static fn () => 'bar'
)
->shouldIterateAs([1 => true]);

$this::fromIterable($input)
->has(
static fn () => 'coin',
static fn () => 'bar'
)
->shouldIterateAs([3 => true]);
}

public function it_can_head(): void
Expand Down
4 changes: 2 additions & 2 deletions src/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -440,9 +440,9 @@ public function groupBy(?callable $callable = null): CollectionInterface
return new self(GroupBy::of()($callable), $this->getIterator());
}

public function has(callable $callback): CollectionInterface
public function has(callable ...$callbacks): CollectionInterface
{
return new self(Has::of()($callback), $this->getIterator());
return new self(Has::of()(...$callbacks), $this->getIterator());
}

public function head(): CollectionInterface
Expand Down
2 changes: 1 addition & 1 deletion src/Contract/Operation/Hasable.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ interface Hasable
*
* @psalm-return \loophp\collection\Collection<int, bool>
*/
public function has(callable $callback): Collection;
public function has(callable ...$callbacks): Collection;
}
51 changes: 39 additions & 12 deletions src/Operation/Has.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace loophp\collection\Operation;

use ArrayIterator;
use Closure;
use Generator;
use Iterator;
Expand All @@ -12,33 +13,59 @@
* @psalm-template TKey
* @psalm-template TKey of array-key
* @psalm-template T
*
* phpcs:disable Generic.Files.LineLength.TooLong
*/
final class Has extends AbstractOperation
{
/**
* @psalm-return Closure(callable(T, TKey, Iterator<TKey, T>): T): Closure(Iterator<TKey, T>): Generator<int, bool>
* @psalm-return Closure(callable(T, TKey, Iterator<TKey, T>): T ...): Closure(Iterator<TKey, T>): Generator<int|TKey, bool>
*/
public function __invoke(): Closure
{
return
/**
* @psalm-param callable(T, TKey, Iterator<TKey, T>): T $callback
* @psalm-param callable(T, TKey, Iterator<TKey, T>): T ...$callbacks
*
* @psalm-return Closure(Iterator<TKey, T>): Generator<int, bool>
* @psalm-return Closure(Iterator<TKey, T>): Generator<int|TKey, bool>
*/
static function (callable $callback): Closure {
$matcher =
static function (callable ...$callbacks): Closure {
return
/**
* @psalm-param T $value
* @psalm-return T
* @psalm-param Iterator<TKey, T> $iterator
*
* @psalm-return Generator<int|TKey, bool>
*/
static fn ($value) => $value;
static function (Iterator $iterator) use ($callbacks): Generator {
$callback =
/**
* @psalm-param callable(T, TKey, Iterator<TKey, T>): bool $callback
*/
static fn (callable $callback): Closure =>
/**
* @param mixed $value
* @psalm-param T $value
*
* @param mixed $key
* @psalm-param TKey $key
*
* @psalm-param Iterator<TKey, T> $iterator
*/
static fn ($value, $key, Iterator $iterator): bool => $callback($value, $key, $iterator) === $value;

/** @psalm-var Closure(Iterator<TKey, T>): Generator<int, bool> $match */
$match = MatchOne::of()($matcher)($callback);
$callbacks = array_map($callback, $callbacks);

// Point free style.
return $match;
foreach ($iterator as $key => $current) {
/** @psalm-var Iterator<int, bool> $result */
$result = MatchOne::of()(static fn () => true)(...$callbacks)(Pair::of()(new ArrayIterator([$key, $current])));

if (true === $result->current()) {
return yield $key => true;
}
}

return yield false;
};
};
}
}

0 comments on commit 88cae79

Please sign in to comment.