Skip to content

Commit

Permalink
refactor(groupbyable)!: simplify implementation and update typing info (
Browse files Browse the repository at this point in the history
  • Loading branch information
Lctrs committed Oct 30, 2021
1 parent c2189f4 commit e9bd67a
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 38 deletions.
8 changes: 3 additions & 5 deletions docs/pages/api.rst
Expand Up @@ -1084,13 +1084,11 @@ Signature: ``Collection::group(): Collection;``
groupBy
~~~~~~~

Group items based on their keys.

The default behaviour can be customized with a callback.
Group items based on the provided callback.

Interface: `GroupByable`_

Signature: ``Collection::groupBy(?callable $callback = null): Collection;``
Signature: ``Collection::groupBy(callable $callback): Collection;``

.. code-block:: php
Expand All @@ -1104,7 +1102,7 @@ Signature: ``Collection::groupBy(?callable $callback = null): Collection;``
};
$collection = Collection::fromIterable($callback())
->groupBy(); // [1 => ['a', 'b', 'c'], 2 => ['d', 'e'], 3 => ['f']]
->groupBy(static fn (string $char, int $key): int => $key); // [1 => ['a', 'b', 'c'], 2 => ['d', 'e'], 3 => ['f']]
has
~~~
Expand Down
7 changes: 1 addition & 6 deletions spec/loophp/collection/CollectionSpec.php
Expand Up @@ -1805,7 +1805,7 @@ public function it_can_groupBy(): void
};

$this::fromCallable($callback)
->groupBy()
->groupBy(static fn (string $value, int $key): int => $key)
->shouldIterateAs([
1 => [
'a',
Expand Down Expand Up @@ -1854,11 +1854,6 @@ public function it_can_groupBy(): void
19,
],
]);

$input = range(0, 20);
$this::fromIterable($input)
->groupBy(static function () {return null; })
->shouldIterateAs($input);
}

public function it_can_has(): void
Expand Down
2 changes: 1 addition & 1 deletion src/Collection.php
Expand Up @@ -556,7 +556,7 @@ public function group(): CollectionInterface
return new self(Group::of(), [$this->getIterator()]);
}

public function groupBy(?callable $callable = null): CollectionInterface
public function groupBy(callable $callable): CollectionInterface
{
return new self(GroupBy::of()($callable), [$this->getIterator()]);
}
Expand Down
8 changes: 6 additions & 2 deletions src/Contract/Operation/GroupByable.php
Expand Up @@ -23,7 +23,11 @@ interface GroupByable
*
* @see https://loophp-collection.readthedocs.io/en/stable/pages/api.html#groupby
*
* @return Collection<TKey, T>
* @template NewTKey of array-key
*
* @param callable(T=, TKey=): NewTKey $callable
*
* @return Collection<NewTKey, non-empty-list<T>>
*/
public function groupBy(?callable $callable = null): Collection;
public function groupBy(callable $callable): Collection;
}
36 changes: 12 additions & 24 deletions src/Operation/GroupBy.php
Expand Up @@ -26,52 +26,40 @@ final class GroupBy extends AbstractOperation
/**
* @pure
*
* @return Closure((null | callable(TKey, T ): (TKey | null))):Closure (Iterator<TKey, T>): Generator<int, T|list<T>>
* @template NewTKey of array-key
*
* @return Closure(callable(T=, TKey=): NewTKey):Closure(Iterator<TKey, T>): Generator<NewTKey, non-empty-list<T>>
*/
public function __invoke(): Closure
{
return
/**
* @param null|callable(TKey, T):(TKey|null) $callable
* @param callable(T=, TKey=): NewTKey $callable
*
* @return Closure(Iterator<TKey, T>): Generator<int, T|list<T>>
* @return Closure(Iterator<TKey, T>): Generator<NewTKey, non-empty-list<T>>
*/
static function (?callable $callable = null): Closure {
/** @var callable(T, TKey): (TKey|null) $callable */
$callable = $callable ??
/**
* @param T $value
* @param TKey $key
*
* @return TKey
*/
static fn ($value, $key) => $key;

static function (callable $callable): Closure {
$reducerFactory =
/**
* @param callable(T, TKey): (TKey|null) $callback
* @param callable(T=, TKey=): NewTKey $callback
*
* @return Closure(array<TKey, T|list<T>>, T, TKey): array<TKey, T|list<T>>
* @return Closure(array<NewTKey, non-empty-list<T>>, T, TKey): array<NewTKey, non-empty-list<T>>
*/
static fn (callable $callback): Closure =>
/**
* @param array<TKey, list<T>> $collect
* @param array<NewTKey, non-empty-list<T>> $collect
* @param T $value
* @param TKey $key
*
* @return non-empty-array<TKey, T|list<T>>
* @return non-empty-array<NewTKey, non-empty-list<T>>
*/
static function (array $collect, $value, $key) use ($callback): array {
if (null !== $groupKey = $callback($value, $key)) {
$collect[$groupKey][] = $value;
} else {
$collect[$key] = $value;
}
$collect[$callback($value, $key)][] = $value;

return $collect;
};

/** @var Closure(Iterator<TKey, T>): Generator<int, list<T>> $pipe */
/** @var Closure(Iterator<TKey, T>): Generator<NewTKey, non-empty-list<T>> $pipe */
$pipe = Pipe::of()(
Reduce::of()($reducerFactory($callable))([]),
Flatten::of()(1)
Expand Down
47 changes: 47 additions & 0 deletions tests/static-analysis/groupbyable.php
@@ -0,0 +1,47 @@
<?php

/**
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/

declare(strict_types=1);

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

use loophp\collection\Collection;
use loophp\collection\Contract\Collection as CollectionInterface;

/**
* @param CollectionInterface<int, non-empty-list<string>> $collection
*/
function groupby_sameKeyType(CollectionInterface $collection): void
{
}

/**
* @param CollectionInterface<string, non-empty-list<string>> $collection
*/
function groupby_newKeyType(CollectionInterface $collection): void
{
}

$foo = [
0 => 'foo',
1 => 'bar',
2 => 'baz',
];

groupby_sameKeyType(
Collection::fromIterable($foo)->groupBy(static fn (string $value, int $key): int => $key)
);

groupby_newKeyType(
Collection::fromIterable($foo)->groupBy(static fn (string $value): string => $value)
);

// VALID failure -> invalid key types
/** @psalm-suppress InvalidArgument @phpstan-ignore-next-line */
Collection::fromIterable($foo)->groupBy(static fn () => null);
/** @psalm-suppress InvalidArgument @phpstan-ignore-next-line */
Collection::fromIterable($foo)->groupBy(static fn (): stdClass => new stdClass());

0 comments on commit e9bd67a

Please sign in to comment.