Skip to content

Commit

Permalink
feat: Add Partition command.
Browse files Browse the repository at this point in the history
  • Loading branch information
drupol committed Feb 11, 2021
1 parent bc6d61e commit 97f8479
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 0 deletions.
17 changes: 17 additions & 0 deletions docs/pages/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1270,6 +1270,22 @@ Signature: ``Collection::pair();``
// ]
// ]
partition
~~~~~~~~~

With one or multiple callable, partition the items into 2 subgroups of items.

.. 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
same operation.

Interface: `Partitionable`_

Signature: ``Collection::partition(callable ...$callbacks);``

.. literalinclude:: code/operations/partition.php
:language: php

permutate
~~~~~~~~~

Expand Down Expand Up @@ -2162,6 +2178,7 @@ Signature: ``Collection::zip(iterable ...$iterables);``
.. _Packable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Packable.php
.. _Padable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Padable.php
.. _Pairable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Pairable.php
.. _Partitionable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Partitionable.php
.. _Permutateable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Permutateable.php
.. _Pluckable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Pluckable.php
.. _Prependable: https://github.com/loophp/collection/blob/master/src/Contract/Operation/Prependable.php
Expand Down
42 changes: 42 additions & 0 deletions docs/pages/code/operations/partition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace App;

include __DIR__ . '/../vendor/autoload.php';

use Closure;
use loophp\collection\Collection;

$isGreaterThan = static fn (int $left): Closure => static fn (int $right): bool => $left < $right;

$input = array_combine(range('a', 'l'), [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3]);

$collection = Collection::fromIterable($input)
->partition(
$isGreaterThan(5),
$isGreaterThan(3)
);

// Result
/*
[
[
['d', 4],
['e', 5],
['f', 6],
['g', 7],
['h', 8],
['i', 9],
],
[
['a', 1],
['b', 2],
['c', 3],
['j', 1],
['k', 2],
['l', 3],
],
]
*/
31 changes: 31 additions & 0 deletions spec/loophp/collection/CollectionSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -1857,6 +1857,37 @@ public function it_can_pair(): void
->shouldIterateAs($gen());
}

public function it_can_partition(): void
{
$isGreaterThan = static fn (int $left): Closure => static fn (int $right): bool => $left < $right;

$input = array_combine(range('a', 'l'), [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3]);

$this::fromIterable($input)
->partition(
$isGreaterThan(5),
$isGreaterThan(3)
)
->shouldIterateAs([
[
['d', 4],
['e', 5],
['f', 6],
['g', 7],
['h', 8],
['i', 9],
],
[
['a', 1],
['b', 2],
['c', 3],
['j', 1],
['k', 2],
['l', 3],
],
]);
}

public function it_can_permutate(): void
{
$this::fromIterable(range('a', 'c'))
Expand Down
6 changes: 6 additions & 0 deletions src/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
use loophp\collection\Operation\Pack;
use loophp\collection\Operation\Pad;
use loophp\collection\Operation\Pair;
use loophp\collection\Operation\Partition;
use loophp\collection\Operation\Permutate;
use loophp\collection\Operation\Pipe;
use loophp\collection\Operation\Pluck;
Expand Down Expand Up @@ -560,6 +561,11 @@ public function pair(): CollectionInterface
return new self(Pair::of(), $this->getIterator());
}

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

public function permutate(): CollectionInterface
{
return new self(Permutate::of(), $this->getIterator());
Expand Down
2 changes: 2 additions & 0 deletions src/Contract/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
use loophp\collection\Contract\Operation\Packable;
use loophp\collection\Contract\Operation\Padable;
use loophp\collection\Contract\Operation\Pairable;
use loophp\collection\Contract\Operation\Partitionable;
use loophp\collection\Contract\Operation\Permutateable;
use loophp\collection\Contract\Operation\Pipeable;
use loophp\collection\Contract\Operation\Pluckable;
Expand Down Expand Up @@ -275,6 +276,7 @@ interface Collection extends
Packable,
Padable,
Pairable,
Partitionable,
Permutateable,
Pipeable,
Pluckable,
Expand Down
23 changes: 23 additions & 0 deletions src/Contract/Operation/Partitionable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace loophp\collection\Contract\Operation;

use loophp\collection\Contract\Collection;

/**
* @psalm-template TKey
* @psalm-template TKey of array-key
* @psalm-template T
*/
interface Partitionable
{
/**
* @param callable ...$callbacks
* @psalm-param callable(T, TKey):bool ...$callbacks
*
* @psalm-return \loophp\collection\Collection<int, array{int, array<0: TKey, 1: T>}>
*/
public function partition(callable ...$callbacks): Collection;
}
84 changes: 84 additions & 0 deletions src/Operation/Partition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

declare(strict_types=1);

namespace loophp\collection\Operation;

use Closure;
use Generator;
use Iterator;

/**
* @psalm-template TKey
* @psalm-template TKey of array-key
* @psalm-template T
*
* phpcs:disable Generic.Files.LineLength.TooLong
*/
final class Partition extends AbstractOperation
{
/**
* @psalm-return Closure(callable(T, TKey, Iterator<TKey, T>):bool...): Closure(Iterator<TKey, T>): Generator<int, list<array{0: TKey, 1: T}>>
*/
public function __invoke(): Closure
{
return
/**
* @psalm-param callable(T, TKey, Iterator<TKey, T>):bool ...$callbacks
*
* @psalm-return Closure(Iterator<TKey, T>): Generator<int, list<array{0: TKey, 1: T}>>
*/
static fn (callable ...$callbacks): Closure =>
/**
* @psalm-param Iterator<TKey, T> $iterator
*
* @psalm-return Generator<int, list<array{0: TKey, 1: T}>>
*/
static function (Iterator $iterator) use ($callbacks): Generator {
$reducerCallback =
/**
* @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 ($key): Closure =>
/**
* @param mixed $current
* @psalm-param T $current
*
* @psalm-return Closure(Iterator<TKey, T>): Closure(bool, callable(T, TKey, Iterator<TKey, T>): bool): bool
*/
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);

$true = $false = [];

foreach ($iterator as $key => $current) {
$result = array_reduce(
$callbacks,
$reducerCallback($key)($current)($iterator),
false
);

$result ?
$true[] = [$key, $current] :
$false[] = [$key, $current];
}

yield $true;

return yield $false;
};
}
}

0 comments on commit 97f8479

Please sign in to comment.