diff --git a/src/Psl/Arr/filter.php b/src/Psl/Arr/filter.php new file mode 100644 index 0000000..58a6082 --- /dev/null +++ b/src/Psl/Arr/filter.php @@ -0,0 +1,45 @@ + 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 $array + * @psalm-param (pure-callable(Tv): bool)|null $predicate + * + * @psalm-return array + * + * @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; +} diff --git a/src/Psl/Arr/filter_keys.php b/src/Psl/Arr/filter_keys.php new file mode 100644 index 0000000..efe6e9b --- /dev/null +++ b/src/Psl/Arr/filter_keys.php @@ -0,0 +1,45 @@ + '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 $array + * @psalm-param (pure-callable(Tk): bool)|null $predicate + * + * @psalm-return array + * + * @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; +} diff --git a/src/Psl/Arr/filter_nulls.php b/src/Psl/Arr/filter_nulls.php new file mode 100644 index 0000000..8ae58af --- /dev/null +++ b/src/Psl/Arr/filter_nulls.php @@ -0,0 +1,33 @@ + Arr(1, 5) + * + * @psalm-template T + * + * @psalm-param list $array + * + * @psalm-return list + * + * @psalm-pure + */ +function filter_nulls(array $array): array +{ + /** @psalm-var list $result */ + $result = []; + foreach ($array as $value) { + if (null !== $value) { + $result[] = $value; + } + } + + return $result; +} diff --git a/src/Psl/Arr/filter_with_key.php b/src/Psl/Arr/filter_with_key.php new file mode 100644 index 0000000..2fd1385 --- /dev/null +++ b/src/Psl/Arr/filter_with_key.php @@ -0,0 +1,56 @@ + 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 $array + * @psalm-param (pure-callable(Tk, Tv): bool)|null $predicate + * + * @psalm-return array + * + * @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 $result */ + $result = []; + foreach ($array as $k => $v) { + if ($predicate($k, $v)) { + $result[$k] = $v; + } + } + + return $result; +} diff --git a/src/Psl/Internal/Loader.php b/src/Psl/Internal/Loader.php index 22d55b8..d549dc6 100644 --- a/src/Psl/Internal/Loader.php +++ b/src/Psl/Internal/Loader.php @@ -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', diff --git a/tests/Psl/Arr/FilterKeysTest.php b/tests/Psl/Arr/FilterKeysTest.php new file mode 100644 index 0000000..c739490 --- /dev/null +++ b/tests/Psl/Arr/FilterKeysTest.php @@ -0,0 +1,30 @@ + '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]; + } +} diff --git a/tests/Psl/Arr/FilterNullsTest.php b/tests/Psl/Arr/FilterNullsTest.php new file mode 100644 index 0000000..0dfffb1 --- /dev/null +++ b/tests/Psl/Arr/FilterNullsTest.php @@ -0,0 +1,20 @@ + false]; + yield [['a', 'b'], ['a', 'b'], fn (string $_): bool => true]; + yield [['a'], ['a', 'b'], fn (string $v): bool => 'b' !== $v]; + } +} diff --git a/tests/Psl/Arr/FilterWithKeyTest.php b/tests/Psl/Arr/FilterWithKeyTest.php new file mode 100644 index 0000000..095c74d --- /dev/null +++ b/tests/Psl/Arr/FilterWithKeyTest.php @@ -0,0 +1,34 @@ + 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]; + } +}