From b33d6b2003efef272e4c6741e1d26e960a92622d Mon Sep 17 00:00:00 2001 From: Saif Eddin Gmati <29315886+azjezz@users.noreply.github.com> Date: Fri, 19 Feb 2021 21:04:54 +0100 Subject: [PATCH] [Vec] add filter and map functions (#136) --- src/Psl/Dict/filter.php | 8 ++--- src/Psl/Dict/filter_keys.php | 10 +++--- src/Psl/Dict/filter_nulls.php | 32 ++++++++++++++++++ src/Psl/Dict/filter_with_key.php | 8 ++--- src/Psl/Internal/Loader.php | 6 ++++ src/Psl/Vec/filter.php | 42 ++++++++++++++++++++++++ src/Psl/Vec/filter_keys.php | 43 ++++++++++++++++++++++++ src/Psl/Vec/filter_with_key.php | 51 +++++++++++++++++++++++++++++ src/Psl/Vec/map.php | 35 ++++++++++++++++++++ src/Psl/Vec/map_with_key.php | 35 ++++++++++++++++++++ tests/Psl/Dict/FilterNullsTest.php | 35 ++++++++++++++++++++ tests/Psl/Vec/ChunkTest.php | 33 +++++++++++++++++++ tests/Psl/Vec/FilterKeysTest.php | 31 ++++++++++++++++++ tests/Psl/Vec/FilterTest.php | 30 +++++++++++++++++ tests/Psl/Vec/FilterWithKeyTest.php | 34 +++++++++++++++++++ tests/Psl/Vec/MapTest.php | 29 ++++++++++++++++ tests/Psl/Vec/MapWithKeyTest.php | 30 +++++++++++++++++ tests/Psl/Vec/ZipTest.php | 40 ++++++++++++++++++++++ 18 files changed, 519 insertions(+), 13 deletions(-) create mode 100644 src/Psl/Dict/filter_nulls.php create mode 100644 src/Psl/Vec/filter.php create mode 100644 src/Psl/Vec/filter_keys.php create mode 100644 src/Psl/Vec/filter_with_key.php create mode 100644 src/Psl/Vec/map.php create mode 100644 src/Psl/Vec/map_with_key.php create mode 100644 tests/Psl/Dict/FilterNullsTest.php create mode 100644 tests/Psl/Vec/ChunkTest.php create mode 100644 tests/Psl/Vec/FilterKeysTest.php create mode 100644 tests/Psl/Vec/FilterTest.php create mode 100644 tests/Psl/Vec/FilterWithKeyTest.php create mode 100644 tests/Psl/Vec/MapTest.php create mode 100644 tests/Psl/Vec/MapWithKeyTest.php create mode 100644 tests/Psl/Vec/ZipTest.php diff --git a/src/Psl/Dict/filter.php b/src/Psl/Dict/filter.php index fd5273b..b3f25c2 100644 --- a/src/Psl/Dict/filter.php +++ b/src/Psl/Dict/filter.php @@ -14,11 +14,11 @@ use Closure; * * Example: * - * Arr\filter(['', '0', 'a', 'b']) - * => Arr('a', 'b') + * Dict\filter(['', '0', 'a', 'b']) + * => Dict(2 => 'a', 3 => 'b') * - * Arr\filter(['foo', 'bar', 'baz', 'qux'], fn(string $value): bool => Str\contains($value, 'a')); - * => Arr('bar', 'baz') + * Dict\filter(['foo', 'bar', 'baz', 'qux'], fn(string $value): bool => Str\contains($value, 'a')); + * => Dict(1 => 'bar', 2 => 'baz') * * @psalm-template Tk of array-key * @psalm-template Tv diff --git a/src/Psl/Dict/filter_keys.php b/src/Psl/Dict/filter_keys.php index 9bd373e..c6c1983 100644 --- a/src/Psl/Dict/filter_keys.php +++ b/src/Psl/Dict/filter_keys.php @@ -7,18 +7,18 @@ namespace Psl\Dict; use Closure; /** - * Returns an array containing only the keys for which the given predicate + * Returns a dict 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') + * Dict\filter_keys([0 => 'a', 1 => 'b']) + * => Dict(1 => 'b') * - * Arr\filter_keys([0 => 'a', 1 => 'b', 2 => 'c'], fn(int $key): bool => $key <= 1); - * => Arr(0 => 'a', 1 => 'b') + * Dict\filter_keys([0 => 'a', 1 => 'b', 2 => 'c'], fn(int $key): bool => $key <= 1); + * => Dict(0 => 'a', 1 => 'b') * * @psalm-template Tk of array-key * @psalm-template Tv diff --git a/src/Psl/Dict/filter_nulls.php b/src/Psl/Dict/filter_nulls.php new file mode 100644 index 0000000..08c19f2 --- /dev/null +++ b/src/Psl/Dict/filter_nulls.php @@ -0,0 +1,32 @@ + Dict(0 => 1, 2 => 5) + * + * @psalm-template Tk of array-key + * @psalm-template Tv + * + * @psalm-param iterable $iterable + * + * @psalm-return array + */ +function filter_nulls(iterable $iterable): array +{ + /** @var array $result */ + $result = []; + foreach ($iterable as $key => $value) { + if (null !== $value) { + $result[$key] = $value; + } + } + + return $result; +} diff --git a/src/Psl/Dict/filter_with_key.php b/src/Psl/Dict/filter_with_key.php index 6148e14..0364171 100644 --- a/src/Psl/Dict/filter_with_key.php +++ b/src/Psl/Dict/filter_with_key.php @@ -14,14 +14,14 @@ use Psl; * * Example: * - * Arr\filter_with_key(['a', '0', 'b', 'c']) - * => Iter('b', 'c') + * Dict\filter_with_key(['a', '0', 'b', 'c']) + * => Dict(2 => 'b', 3 => 'c') * - * Arr\filter_with_key( + * Dict\filter_with_key( * ['foo', 'bar', 'baz', 'qux'], * fn(int $key, string $value): bool => $key > 1 && Str\contains($value, 'a') * ); - * => Arr('baz') + * => Dict(2 => 'baz') * * @psalm-template Tk of array-key * @psalm-template Tv diff --git a/src/Psl/Internal/Loader.php b/src/Psl/Internal/Loader.php index d5059ca..d5ed75d 100644 --- a/src/Psl/Internal/Loader.php +++ b/src/Psl/Internal/Loader.php @@ -102,6 +102,7 @@ final class Loader 'Psl\Dict\drop_while', 'Psl\Dict\equal', 'Psl\Dict\filter', + 'Psl\Dict\filter_nulls', 'Psl\Dict\filter_keys', 'Psl\Dict\filter_with_key', 'Psl\Dict\flatten', @@ -199,12 +200,17 @@ final class Loader 'Psl\Vec\concat', 'Psl\Vec\enumerate', 'Psl\Vec\fill', + 'Psl\Vec\filter', + 'Psl\Vec\filter_keys', 'Psl\Vec\filter_nulls', + 'Psl\Vec\filter_with_key', 'Psl\Vec\flat_map', 'Psl\Vec\keys', 'Psl\Vec\partition', 'Psl\Vec\range', 'Psl\Vec\reductions', + 'Psl\Vec\map', + 'Psl\Vec\map_with_key', 'Psl\Vec\reproduce', 'Psl\Vec\reverse', 'Psl\Vec\shuffle', diff --git a/src/Psl/Vec/filter.php b/src/Psl/Vec/filter.php new file mode 100644 index 0000000..4200745 --- /dev/null +++ b/src/Psl/Vec/filter.php @@ -0,0 +1,42 @@ + Vec('a', 'b') + * + * Vec\filter(['foo', 'bar', 'baz', 'qux'], fn(string $value): bool => Str\contains($value, 'a')); + * => Vec('bar', 'baz') + * + * @psalm-template T + * + * @psalm-param iterable $iterable + * @psalm-param (callable(T): bool)|null $predicate + * + * @psalm-return list + */ +function filter(iterable $iterable, ?callable $predicate = null): array +{ + /** @psalm-var (callable(T): bool) $predicate */ + $predicate = $predicate ?? Closure::fromCallable('Psl\Internal\boolean'); + $result = []; + foreach ($iterable as $v) { + if ($predicate($v)) { + $result[] = $v; + } + } + + return $result; +} diff --git a/src/Psl/Vec/filter_keys.php b/src/Psl/Vec/filter_keys.php new file mode 100644 index 0000000..93be422 --- /dev/null +++ b/src/Psl/Vec/filter_keys.php @@ -0,0 +1,43 @@ + 'a', 1 => 'b']) + * => Vec('b') + * + * Vec\filter_keys([0 => 'a', 1 => 'b', 2 => 'c'], fn(int $key): bool => $key <= 1); + * => Vec('a', 'b') + * + * @psalm-template Tk + * @psalm-template Tv + * + * @psalm-param iterable $iterable + * @psalm-param (callable(Tk): bool)|null $predicate + * + * @psalm-return list + */ +function filter_keys(iterable $iterable, ?callable $predicate = null): array +{ + /** @psalm-var (callable(Tk): bool) $predicate */ + $predicate = $predicate ?? Closure::fromCallable('Psl\Internal\boolean'); + $result = []; + foreach ($iterable as $k => $v) { + if ($predicate($k)) { + $result[] = $v; + } + } + + return $result; +} diff --git a/src/Psl/Vec/filter_with_key.php b/src/Psl/Vec/filter_with_key.php new file mode 100644 index 0000000..fa94d3c --- /dev/null +++ b/src/Psl/Vec/filter_with_key.php @@ -0,0 +1,51 @@ + Vec('b', 'c') + * + * Vec\filter_with_key( + * ['foo', 'bar', 'baz', 'qux'], + * fn(int $key, string $value): bool => $key > 1 && Str\contains($value, 'a') + * ); + * => Vec('baz') + * + * @psalm-template Tk + * @psalm-template Tv + * + * @psalm-param iterable $iterable + * @psalm-param (callable(Tk, Tv): bool)|null $predicate + * + * @psalm-return list + */ +function filter_with_key(iterable $iterable, ?callable $predicate = null): array +{ + $predicate = $predicate ?? + /** + * @psalm-param Tk $k + * @psalm-param Tv $v + */ + static fn ($k, $v): bool => Psl\Internal\boolean($v); + + $result = []; + foreach ($iterable as $k => $v) { + if ($predicate($k, $v)) { + $result[] = $v; + } + } + + return $result; +} diff --git a/src/Psl/Vec/map.php b/src/Psl/Vec/map.php new file mode 100644 index 0000000..f3eeb4f --- /dev/null +++ b/src/Psl/Vec/map.php @@ -0,0 +1,35 @@ + $i * 2); + * => Vec(2, 4, 6, 8, 10) + * + * @psalm-template Tk + * @psalm-template Tv + * @psalm-template T + * + * @psalm-param iterable $iterable Iterable to be mapped over + * @psalm-param (callable(Tv): T) $function + * + * @psalm-return list + */ +function map(iterable $iterable, callable $function): array +{ + $result = []; + foreach ($iterable as $value) { + $result[] = $function($value); + } + + return $result; +} diff --git a/src/Psl/Vec/map_with_key.php b/src/Psl/Vec/map_with_key.php new file mode 100644 index 0000000..6b1b188 --- /dev/null +++ b/src/Psl/Vec/map_with_key.php @@ -0,0 +1,35 @@ + $k + $v); + * => Vec(1, 3, 5, 7, 9) + * + * @psalm-template Tk + * @psalm-template Tv + * @psalm-template T + * + * @psalm-param iterable $iterable Iterable to be mapped over + * @psalm-param (callable(Tk,Tv): T) $function + * + * @psalm-return list + */ +function map_with_key(iterable $iterable, callable $function): array +{ + $result = []; + foreach ($iterable as $key => $value) { + $result[] = $function($key, $value); + } + + return $result; +} diff --git a/tests/Psl/Dict/FilterNullsTest.php b/tests/Psl/Dict/FilterNullsTest.php new file mode 100644 index 0000000..b190a22 --- /dev/null +++ b/tests/Psl/Dict/FilterNullsTest.php @@ -0,0 +1,35 @@ +> $expected + * @psalm-param iterable $iterable + * + * @dataProvider provideData + */ + public function testChunk(array $expected, iterable $iterable, int $size): void + { + static::assertSame($expected, Vec\chunk($iterable, $size)); + } + + public function provideData(): iterable + { + yield [[[1], [2], [3]], Vec\range(1, 3), 1]; + yield [[], [], 4]; + yield [[[1, 2, 3], [4, 5, 6], [7, 8, 9]], Vec\range(1, 9), 3]; + yield [[[1, 3, 5], [7, 9]], Vec\range(1, 9, 2), 3]; + yield [[[1, 3], [5, 7], [9]], Vec\range(1, 9, 2), 2]; + } +} diff --git a/tests/Psl/Vec/FilterKeysTest.php b/tests/Psl/Vec/FilterKeysTest.php new file mode 100644 index 0000000..69145bb --- /dev/null +++ b/tests/Psl/Vec/FilterKeysTest.php @@ -0,0 +1,31 @@ + $k !== 1]; + yield [['b'], ['a', 'b'], static fn (int $k): bool => $k !== 0]; + yield [[], ['a', 'b'], static fn (int $_) => false]; + yield [['a', 'b'], ['a', 'b'], static fn (int $_): bool => true]; + } +} diff --git a/tests/Psl/Vec/FilterTest.php b/tests/Psl/Vec/FilterTest.php new file mode 100644 index 0000000..d46b3f1 --- /dev/null +++ b/tests/Psl/Vec/FilterTest.php @@ -0,0 +1,30 @@ + false]; + yield [['a', 'b'], ['a', 'b'], static fn (string $_): bool => true]; + yield [['a'], ['a', 'b'], static fn (string $v): bool => 'b' !== $v]; + } +} diff --git a/tests/Psl/Vec/FilterWithKeyTest.php b/tests/Psl/Vec/FilterWithKeyTest.php new file mode 100644 index 0000000..5dd8e6c --- /dev/null +++ b/tests/Psl/Vec/FilterWithKeyTest.php @@ -0,0 +1,34 @@ + false]; + yield [['a', 'b'], ['a', 'b'], static fn (int $_k, string $_v): bool => true]; + yield [['a'], ['a', 'b'], static fn (int $k, string $v): bool => 'b' !== $v]; + yield [[], ['a', 'b'], static fn (int $k, string $v): bool => 'b' !== $v && 0 !== $k]; + yield [['a'], ['a', 'b'], static fn (int $k, string $v): bool => 'b' !== $v && 1 !== $k]; + yield [[], ['a', 'b'], static fn (int $k, string $v): bool => 'a' !== $v && 1 !== $k]; + yield [['b'], ['a', 'b'], static fn (int $k, string $v): bool => 'a' !== $v && 0 !== $k]; + } +} diff --git a/tests/Psl/Vec/MapTest.php b/tests/Psl/Vec/MapTest.php new file mode 100644 index 0000000..72881a2 --- /dev/null +++ b/tests/Psl/Vec/MapTest.php @@ -0,0 +1,29 @@ + $v]; + yield [[2, 4, 6], [1, 2, 3], static fn (int $v): int => $v * 2]; + yield [['1', '2', '3'], [1, 2, 3], static fn (int $v): string => (string)$v]; + yield [[], [], static fn (int $v): string => (string)$v]; + } +} diff --git a/tests/Psl/Vec/MapWithKeyTest.php b/tests/Psl/Vec/MapWithKeyTest.php new file mode 100644 index 0000000..6574ff3 --- /dev/null +++ b/tests/Psl/Vec/MapWithKeyTest.php @@ -0,0 +1,30 @@ + 1, 'b' => 2, 'c' => 3], static fn (string $k, int $v): int => $v]; + yield [[1, 3, 5], [1, 2, 3], static fn (int $k, int $v): int => $k + $v]; + yield [[0, 4, 16], [1, 2, 3], static fn (int $k, int $v): int => $k * (2 ** $v)]; + yield [['1', '3', '5'], [1, 2, 3], static fn (int $k, int $v): string => (string) ($k + $v)]; + yield [[], [], static fn (int $k, int $v): string => (string) ($k + $v)]; + } +} diff --git a/tests/Psl/Vec/ZipTest.php b/tests/Psl/Vec/ZipTest.php new file mode 100644 index 0000000..1a1ccc0 --- /dev/null +++ b/tests/Psl/Vec/ZipTest.php @@ -0,0 +1,40 @@ +