Skip to content

Commit

Permalink
Fix Column Typing + SA Check (#166)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexandruGG committed Jul 31, 2021
1 parent 0175278 commit 7432a68
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 15 deletions.
6 changes: 6 additions & 0 deletions docs/pages/api.rst
Expand Up @@ -463,6 +463,9 @@ column

Return the values from a single column in the input iterables.

.. tip:: If the iterables you are selecting from are ``Generators``, the operation will allow
accessing keys of any type, not just ``int|string``.

Interface: `Columnable`_

Signature: ``Collection::column($column): Collection;``
Expand Down Expand Up @@ -494,6 +497,9 @@ Signature: ``Collection::column($column): Collection;``
$result = Collection::fromIterable($records)
->column('first_name'); // ['John', 'Sally', 'Jane', 'Peter']
$result = Collection::fromIterable($records)
->column('non_existent_key'); // []
combinate
~~~~~~~~~
Expand Down
22 changes: 14 additions & 8 deletions spec/loophp/collection/CollectionSpec.php
Expand Up @@ -660,14 +660,20 @@ public function it_can_column(): void

$this::fromIterable($records)
->column('first_name')
->shouldIterateAs(
[
0 => 'John',
1 => 'Sally',
2 => 'Jane',
3 => 'Peter',
]
);
->shouldIterateAs([0 => 'John', 1 => 'Sally', 2 => 'Jane', 3 => 'Peter']);

$this::fromIterable($records)
->column('middle_name')
->shouldIterateAs([]);

$nonArrayKeyRecords = [
(static fn () => yield ['id'] => 1234)(),
(static fn () => yield ['id'] => 4567)(),
];

$this::fromIterable($nonArrayKeyRecords)
->column(['id'])
->shouldIterateAs([0 => 1234, 1 => 4567]);
}

public function it_can_combinate(): void
Expand Down
4 changes: 2 additions & 2 deletions src/Contract/Operation/Columnable.php
Expand Up @@ -22,9 +22,9 @@ interface Columnable
*
* @see https://loophp-collection.readthedocs.io/en/stable/pages/api.html#column
*
* @param int|string $column
* @param mixed $column
*
* @return Collection<TKey, T>
* @return Collection<int, mixed>
*/
public function column($column): Collection;
}
10 changes: 5 additions & 5 deletions src/Operation/Column.php
Expand Up @@ -24,20 +24,20 @@ final class Column extends AbstractOperation
/**
* @pure
*
* @return Closure(T): Closure(Iterator<TKey, T>): Generator<int, iterable<TKey, T>>
* @return Closure(mixed): Closure(Iterator<TKey, T>): Generator<int, mixed>
*/
public function __invoke(): Closure
{
return
/**
* @param T $column
* @param mixed $column
*
* @return Closure(Iterator<TKey, T>): Generator<int, iterable<TKey, T>>
* @return Closure(Iterator<TKey, T>): Generator<int, mixed>
*/
static function ($column): Closure {
$filterCallbackBuilder =
/**
* @param T $column
* @param mixed $column
*/
static fn ($column): Closure =>
/**
Expand All @@ -47,7 +47,7 @@ static function ($column): Closure {
*/
static fn ($value, $key, Iterator $iterator): bool => $key === $column;

/** @var Closure(Iterator<TKey, T>): Generator<int, iterable<TKey, T>> $pipe */
/** @var Closure(Iterator<TKey, T>): Generator<int, mixed> $pipe */
$pipe = Pipe::of()(
Transpose::of(),
Filter::of()($filterCallbackBuilder($column)),
Expand Down
61 changes: 61 additions & 0 deletions tests/static-analysis/column.php
@@ -0,0 +1,61 @@
<?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, int> $collection
*/
function column_checkInt(CollectionInterface $collection): void
{
}

/**
* @param CollectionInterface<int, string> $collection
*/
function column_checkString(CollectionInterface $collection): void
{
}

$records = [
[
'id' => 2135,
'first_name' => 'John',
'last_name' => 'Doe',
],
[
'id' => 3245,
'first_name' => 'Sally',
'last_name' => 'Smith',
],
];
$nonArrayKeyRecords = [
(static fn () => yield ['id'] => 1234)(),
(static fn () => yield ['id'] => 5678)(),
];

column_checkInt(Collection::fromIterable($records)->column('id'));
column_checkInt(Collection::fromIterable($nonArrayKeyRecords)->column('id'));
column_checkInt(
Collection::fromIterable($records)
->column('id')
->map(static fn (string $id): int => (int) $id)
);

column_checkString(Collection::fromIterable($records)->column('first_name'));
column_checkString(Collection::fromIterable($records)->column('middle_name'));

// Below are examples of usages that are technically incorrect but are allowed
// by static analysis because `column` needs to be flexible and cannot be properly typed

column_checkInt(Collection::fromIterable($records)->column(['first_name']));
column_checkString(Collection::fromIterable($nonArrayKeyRecords)->column('id'));

0 comments on commit 7432a68

Please sign in to comment.