Skip to content

Commit

Permalink
refactor: Update Every operation.
Browse files Browse the repository at this point in the history
BREAKING CHANGE: yes
  • Loading branch information
drupol committed Jan 8, 2021
1 parent d1bdbdf commit fb542be
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 32 deletions.
8 changes: 6 additions & 2 deletions docs/pages/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -545,11 +545,15 @@ Signature: ``Collection::duplicate();``
every
~~~~~

This operation tests whether all elements in the collection pass the test implemented by the provided callback.
This operation tests whether all elements in the collection pass the test implemented by the provided callback(s).

.. 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: `Everyable`_

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

.. code-block:: php
Expand Down
30 changes: 23 additions & 7 deletions spec/loophp/collection/CollectionSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -752,33 +752,49 @@ public function it_can_duplicate(): void

public function it_can_every(): void
{
$input = range(0, 10);

$callback = static function ($value): bool {
return 20 > $value;
};

$this::fromIterable(range(0, 10))
$this::fromIterable($input)
->every($callback)
->shouldIterateAs([10 => true]);
->shouldIterateAs([0 => true]);

$this::empty()
->every($callback)
->shouldIterateAs([true]);
->shouldIterateAs([0 => true]);

$this::fromIterable(range(0, 10))
$this::fromIterable($input)
->every(
static function ($value, $key, Iterator $iterator): bool {
return is_numeric($key);
}
)
->shouldIterateAs([10 => true]);
->shouldIterateAs([0 => true]);

$this::fromIterable(range(0, 10))
$this::fromIterable($input)
->every(
static function ($value, $key, Iterator $iterator): bool {
return $iterator instanceof Iterator;
}
)
->shouldIterateAs([10 => true]);
->shouldIterateAs([0 => true]);

$callback1 = static fn ($value, $key): bool => 20 > $value;
$this::fromIterable($input)
->every($callback1)
->shouldIterateAs([0 => true]);

$callback2 = static fn ($value, $key): bool => 50 < $value;
$this::fromIterable($input)
->every($callback2)
->shouldIterateAs([0 => false]);

$this::fromIterable($input)
->every($callback2, $callback1)
->shouldIterateAs([0 => true]);
}

public function it_can_explode(): void
Expand Down
4 changes: 2 additions & 2 deletions src/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -290,9 +290,9 @@ public static function empty(): CollectionInterface
return self::fromIterable([]);
}

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

public function explode(...$explodes): CollectionInterface
Expand Down
2 changes: 1 addition & 1 deletion src/Contract/Operation/Everyable.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ interface Everyable
/**
* @psalm-return \loophp\collection\Collection<TKey, bool>
*/
public function every(callable $callback): Collection;
public function every(callable ...$callbacks): Collection;
}
66 changes: 46 additions & 20 deletions src/Operation/Every.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,39 +18,65 @@
final class Every extends AbstractOperation
{
/**
* @psalm-return Closure(callable(T, TKey, Iterator<TKey, T> ): bool):Closure (Iterator<TKey, T>): Generator<TKey, bool>
* @psalm-return Closure(callable(T, TKey, Iterator<TKey, T>): bool ...): Closure(Iterator<TKey, T>): Generator<int|TKey, bool>
*/
public function __invoke(): Closure
{
return
/**
* @psalm-return Closure(Iterator<TKey, T>): Generator<TKey, bool>
* @psalm-param callable(T, TKey, Iterator<TKey, T>): bool ...$callbacks
*/
static function (callable $callback): Closure {
$callbackBuilder =
static function (callable ...$callbacks): Closure {
$reducerCallback =
/**
* @psalm-param callable(T, TKey, Iterator<TKey, T>): bool $callback
* @param mixed $key
* @psalm-param TKey $key
*
* @psalm-return Closure(T): Closure(Iterator<TKey, T>): Closure(bool, callable(T, TKey, Iterator<TKey, T>): bool): bool
*/
static fn (callable $callback): Closure =>
static fn ($key): Closure =>
/**
* @param mixed $carry
* @psalm-param T $carry
* @param mixed $current
* @psalm-param T $current
*
* @param mixed $value
* @psalm-param T $value
*
* @param mixed $key
* @psalm-param TKey $key
*
* @psalm-param Iterator<TKey, T> $iterator
* @psalm-return Closure(Iterator<TKey, T>): Closure(bool, callable(T, TKey, Iterator<TKey, T>): bool): bool
*/
static fn ($carry, $value, $key, Iterator $iterator): bool => $callback($value, $key, $iterator);
static fn ($current): Closure =>
/**
* @psalm-param Iterator<TKey, T> $iterator
*
* @psalm-return Closure(bool, callable(T, TKey, Iterator<TKey, T>): bool): bool
*/
static fn (Iterator $iterator): Closure =>
/**
* @psalm-param bool $carry
* @psalm-param callable(T, TKey, Iterator<TKey, T>): bool $callable
*/
static fn (bool $carry, callable $callable): bool => $carry || ($callable($current, $key, $iterator));

return
/**
* @psalm-param Iterator<TKey, T> $iterator
*
* @psalm-return Generator<int|TKey, bool>
*/
static function (Iterator $iterator) use ($callbacks, $reducerCallback): Generator {
// We could use FoldLeft but there is no need to go through all the items,
// we just need to return false as soon as the callbacks returns false.
foreach ($iterator as $key => $value) {
$result = array_reduce(
$callbacks,
$reducerCallback($key)($value)($iterator),
false
);

/** @psalm-var Closure(Iterator<TKey, T>): Generator<TKey, bool> $foldLeft */
$foldLeft = FoldLeft::of()($callbackBuilder($callback))(true);
if (false === $result) {
return yield $key => false;
}
}

// Point free style.
return $foldLeft;
return yield true;
};
};
}
}

0 comments on commit fb542be

Please sign in to comment.