[Arr] add pure implementations of Iter\filter{_keys, nulls, _with_key}

This commit is contained in:
azjezz 2020-09-02 03:59:51 +01:00
parent 795d8793ac
commit 95e0cb37ad
9 changed files with 297 additions and 0 deletions

45
src/Psl/Arr/filter.php Normal file
View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Psl\Arr;
use Closure;
/**
* Returns an array containing only the values for which the given predicate
* returns `true`.
*
* The default predicate is casting the value to boolean.
*
* Example:
*
* Arr\filter(['', '0', 'a', 'b'])
* => Arr('a', 'b')
*
* Arr\filter(['foo', 'bar', 'baz', 'qux'], fn(string $value): bool => Str\contains($value, 'a'));
* => Arr('bar', 'baz')
*
* @psalm-template Tk of array-key
* @psalm-template Tv
*
* @psalm-param array<Tk, Tv> $array
* @psalm-param (pure-callable(Tv): bool)|null $predicate
*
* @psalm-return array<Tk, Tv>
*
* @psalm-pure
*/
function filter(array $array, ?callable $predicate = null): array
{
/** @psalm-var (pure-callable(Tv): bool) $predicate */
$predicate = $predicate ?? Closure::fromCallable('Psl\Internal\boolean');
$result = [];
foreach ($array as $k => $v) {
if ($predicate($v)) {
$result[$k] = $v;
}
}
return $result;
}

View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Psl\Arr;
use Closure;
/**
* Returns an array containing only the keys for which the given predicate
* returns `true`.
*
* The default predicate is casting the key to boolean.
*
* Example:
*
* Arr\filter_keys([0 => 'a', 1 => 'b'])
* => Arr(1 => 'b')
*
* Arr\filter_keys([0 => 'a', 1 => 'b', 2 => 'c'], fn(int $key): bool => $key <= 1);
* => Arr(0 => 'a', 1 => 'b')
*
* @psalm-template Tk of array-key
* @psalm-template Tv
*
* @psalm-param array<Tk, Tv> $array
* @psalm-param (pure-callable(Tk): bool)|null $predicate
*
* @psalm-return array<Tk, Tv>
*
* @psalm-pure
*/
function filter_keys(array $array, ?callable $predicate = null): array
{
/** @psalm-var (pure-callable(Tk): bool) $predicate */
$predicate = $predicate ?? Closure::fromCallable('Psl\Internal\boolean');
$result = [];
foreach ($array as $k => $v) {
if ($predicate($k)) {
$result[$k] = $v;
}
}
return $result;
}

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Psl\Arr;
/**
* Filter out null values from the given array.
*
* Example:
* Arr\filter_nulls([1, null, 5])
* => Arr(1, 5)
*
* @psalm-template T
*
* @psalm-param list<T|null> $array
*
* @psalm-return list<T>
*
* @psalm-pure
*/
function filter_nulls(array $array): array
{
/** @psalm-var list<T> $result */
$result = [];
foreach ($array as $value) {
if (null !== $value) {
$result[] = $value;
}
}
return $result;
}

View File

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Psl\Arr;
use Psl;
/**
* Returns an array containing only the keys and values for which the given predicate
* returns `true`.
*
* The default predicate is casting both they key and the value to boolean.
*
* Example:
*
* Arr\filter_with_key(['a', '0', 'b', 'c'])
* => Iter('b', 'c')
*
* Arr\filter_with_key(
* ['foo', 'bar', 'baz', 'qux'],
* fn(int $key, string $value): bool => $key > 1 && Str\contains($value, 'a')
* );
* => Arr('baz')
*
* @psalm-template Tk of array-key
* @psalm-template Tv
*
* @psalm-param array<Tk, Tv> $array
* @psalm-param (pure-callable(Tk, Tv): bool)|null $predicate
*
* @psalm-return array<Tk, Tv>
*
* @psalm-pure
*/
function filter_with_key(array $array, ?callable $predicate = null): array
{
$predicate = $predicate ??
/**
* @psalm-param Tk $k
* @psalm-param Tv $v
*
* @psalm-pure
*/
fn ($k, $v): bool => Psl\Internal\boolean($v);
/** @psalm-var array<Tk, Tv> $result */
$result = [];
foreach ($array as $k => $v) {
if ($predicate($k, $v)) {
$result[$k] = $v;
}
}
return $result;
}

View File

@ -86,6 +86,10 @@ final class Loader
'Psl\Arr\slice',
'Psl\Arr\take',
'Psl\Arr\take_while',
'Psl\Arr\filter',
'Psl\Arr\filter_keys',
'Psl\Arr\filter_nulls',
'Psl\Arr\filter_with_key',
'Psl\Asio\wrap',
'Psl\Internal\boolean',
'Psl\Internal\type',

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Psl\Tests\Arr;
use PHPUnit\Framework\TestCase;
use Psl\Arr;
class FilterKeysTest extends TestCase
{
/**
* @dataProvider provideData
*/
public function testFilterKeys(array $expected, array $array, ?callable $predicate = null): void
{
$result = Arr\filter_keys($array, $predicate);
self::assertSame($expected, $result);
}
public function provideData(): iterable
{
yield [[], []];
yield [[1 => 'b'], ['a', 'b']];
yield [[], ['a', 'b'], fn () => false];
yield [['a', 'b'], ['a', 'b'], fn (int $_): bool => true];
yield [['a'], ['a', 'b'], fn (int $k): bool => 1 !== $k];
}
}

View File

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Psl\Tests\Arr;
use PHPUnit\Framework\TestCase;
use Psl\Arr;
class FilterNullsTest extends TestCase
{
public function testFilterNulls(): void
{
self::assertCount(0, Arr\filter_nulls([]));
self::assertCount(0, Arr\filter_nulls([null, null]));
self::assertCount(1, Arr\filter_nulls([null, false]));
self::assertCount(1, Arr\filter_nulls([null, 'null']));
self::assertCount(1, Arr\filter_nulls(['null']));
}
}

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Psl\Tests\Arr;
use PHPUnit\Framework\TestCase;
use Psl\Arr;
class FilterTest extends TestCase
{
/**
* @dataProvider provideData
*/
public function testFilter(array $expected, array $array, ?callable $predicate = null): void
{
$result = Arr\filter($array, $predicate);
self::assertSame($expected, $result);
}
public function provideData(): iterable
{
yield [[], []];
yield [['a', 'b'], ['a', 'b']];
yield [[], ['a', 'b'], fn () => false];
yield [['a', 'b'], ['a', 'b'], fn (string $_): bool => true];
yield [['a'], ['a', 'b'], fn (string $v): bool => 'b' !== $v];
}
}

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Psl\Tests\Arr;
use PHPUnit\Framework\TestCase;
use Psl\Arr;
class FilterWithKeyTest extends TestCase
{
/**
* @dataProvider provideData
*/
public function testFilterWithKey(array $expected, array $array, ?callable $predicate = null): void
{
$result = Arr\filter_with_key($array, $predicate);
self::assertSame($expected, $result);
}
public function provideData(): iterable
{
yield [[], []];
yield [['a', 'b'], ['a', 'b']];
yield [[], ['a', 'b'], fn (int $_k, string $_v) => false];
yield [['a', 'b'], ['a', 'b'], fn (int $_k, string $_v): bool => true];
yield [['a'], ['a', 'b'], fn (int $k, string $v): bool => 'b' !== $v];
yield [[], ['a', 'b'], fn (int $k, string $v): bool => 'b' !== $v && 0 !== $k];
yield [['a'], ['a', 'b'], fn (int $k, string $v): bool => 'b' !== $v && 1 !== $k];
yield [[], ['a', 'b'], fn (int $k, string $v): bool => 'a' !== $v && 1 !== $k];
yield [[1 => 'b'], ['a', 'b'], fn (int $k, string $v): bool => 'a' !== $v && 0 !== $k];
}
}