Skip to content

Commit

Permalink
feat: introduce search_with_keys and search_with_keys_opt
Browse files Browse the repository at this point in the history
Currently, there's no way to search iterable using also a key
  • Loading branch information
simPod committed Sep 12, 2024
1 parent 86984c9 commit 77e44f0
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 8 deletions.
3 changes: 2 additions & 1 deletion docs/component/iter.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
- [rewindable](./../../src/Psl/Iter/rewindable.php#L20)
- [search](./../../src/Psl/Iter/search.php#L28)
- [search_opt](./../../src/Psl/Iter/search_opt.php#L30)
- [search_opt_k_v](./../../src/Psl/Iter/search_opt_k_v.php#L31)
- [search_with_keys](./../../src/Psl/Iter/search_with_keys.php#L29)
- [search_with_keys_opt](./../../src/Psl/Iter/search_with_keys_opt.php#L31)
- [to_iterator](./../../src/Psl/Iter/to_iterator.php#L19)

#### `Classes`
Expand Down
3 changes: 2 additions & 1 deletion src/Psl/Internal/Loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,9 @@ final class Loader
'Psl\\Iter\\reduce_with_keys' => 'Psl/Iter/reduce_with_keys.php',
'Psl\\Iter\\rewindable' => 'Psl/Iter/rewindable.php',
'Psl\\Iter\\search' => 'Psl/Iter/search.php',
'Psl\\Iter\\search_with_keys' => 'Psl/Iter/search_with_keys.php',
'Psl\\Iter\\search_opt' => 'Psl/Iter/search_opt.php',
'Psl\\Iter\\search_opt_k_v' => 'Psl/Iter/search_opt_k_v.php',
'Psl\\Iter\\search_with_keys_opt' => 'Psl/Iter/search_with_keys_opt.php',
'Psl\\Iter\\to_iterator' => 'Psl/Iter/to_iterator.php',
'Psl\\Vec\\chunk' => 'Psl/Vec/chunk.php',
'Psl\\Vec\\chunk_with_keys' => 'Psl/Vec/chunk_with_keys.php',
Expand Down
38 changes: 38 additions & 0 deletions src/Psl/Iter/search_with_keys.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Psl\Iter;

use Closure;

/**
* Searches an iterable until a predicate returns true, then returns
* the value of the matching element.
*
* Examples:
*
* Iter\search_with_keys(['foo', 'bar', 'baz'], fn($k, $v) => 'baz' === $v)
* => 'baz'
*
* Iter\search_with_keys(['foo', 'bar', 'baz'], fn($k, $v) => 'qux' === $v)
* => null
*
* @template TKey
* @template TValue
*
* @param iterable<TKey, TValue> $iterable The iterable to search
* @param (Closure(TKey, TValue): bool) $predicate
*
* @return TValue|null
*/
function search_with_keys(iterable $iterable, Closure $predicate): mixed
{
foreach ($iterable as $key => $value) {
if ($predicate($key, $value)) {
return $value;
}
}

return null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
*
* Examples:
*
* Iter\search_opt_k_v(['foo', 'bar', 'baz'], fn($k, $v) => 'baz' === $v)
* Iter\search_with_keys_opt(['foo', 'bar', 'baz'], fn($k, $v) => 'baz' === $v)
* => Option::some('baz')
*
* Iter\search_opt_k_v(['foo', 'bar', 'baz'], fn($k, $v) => 'qux' === $v)
* Iter\search_with_keys_opt(['foo', 'bar', 'baz'], fn($k, $v) => 'qux' === $v)
* => Option::none()
*
* @template TKey
Expand All @@ -28,7 +28,7 @@
*
* @return Option<TValue>
*/
function search_opt_k_v(iterable $iterable, Closure $predicate): Option
function search_with_keys_opt(iterable $iterable, Closure $predicate): Option
{
foreach ($iterable as $key => $value) {
if ($predicate($key, $value)) {
Expand Down
43 changes: 43 additions & 0 deletions tests/unit/Iter/SearchWithKeysOptTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace Psl\Tests\Unit\Iter;

use PHPUnit\Framework\TestCase;
use Psl\Iter;

final class SearchWithKeysOptTest extends TestCase
{
/**
* @dataProvider provideDataSome
*/
public function testSearchSome($expected, iterable $iterable, callable $predicate): void
{
static::assertSame($expected, Iter\search_with_keys_opt($iterable, $predicate)->unwrap());
}

public function provideDataSome(): iterable
{
yield ['baz', ['foo', 'bar', 'baz'], static fn (int $k, string $v): bool => 2 === $k && 'baz' === $v];

yield [
'baz',
Iter\to_iterator(['foo', 'bar', 'baz']), static fn (int $k, string $v): bool => 2 === $k && 'baz' === $v
];
}
/**
* @dataProvider provideDataNone
*/
public function testSearchNone(iterable $iterable, callable $predicate): void
{
static::assertTrue(Iter\search_with_keys_opt($iterable, $predicate)->isNone());
}

public function provideDataNone(): iterable
{
yield [[], static fn (int $k, string $v): bool => 'qux' === $v];
yield [Iter\to_iterator([]), static fn (int $k, string $v): bool => 'qux' === $v];
yield [Iter\to_iterator(['foo', 'bar', 'baz']), static fn (int $k, string $v): bool => 'qux' === $v];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
use PHPUnit\Framework\TestCase;
use Psl\Iter;

final class SearchOptKVTest extends TestCase
final class SearchWithKeysTest extends TestCase
{
/**
* @dataProvider provideDataSome
*/
public function testSearchSome($expected, iterable $iterable, callable $predicate): void
{
static::assertSame($expected, Iter\search_opt_k_v($iterable, $predicate)->unwrap());
static::assertSame($expected, Iter\search_with_keys($iterable, $predicate));
}

public function provideDataSome(): iterable
Expand All @@ -31,8 +31,9 @@ public function provideDataSome(): iterable
*/
public function testSearchNone(iterable $iterable, callable $predicate): void
{
static::assertTrue(Iter\search_opt_k_v($iterable, $predicate)->isNone());
static::assertNull(Iter\search_with_keys($iterable, $predicate));
}

public function provideDataNone(): iterable
{
yield [[], static fn (int $k, string $v): bool => 'qux' === $v];
Expand Down

0 comments on commit 77e44f0

Please sign in to comment.