Skip to content

Commit 099b1f6

Browse files
authored
Merge pull request #33 from samsonasik/add-finder-partition
Add Finder::partition() to splits data into 2 arrays: matching and non-matching
2 parents bb81037 + 6e29b11 commit 099b1f6

File tree

4 files changed

+151
-1
lines changed

4 files changed

+151
-1
lines changed

README.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Features
1717

1818
- [x] Verify at least times: `once()`, `twice()`, `times()`
1919
- [x] Verify exact times: `once()`, `twice()`, `times()`
20-
- [x] Search data: `first()`, `last()`, `rows()`
20+
- [x] Search data: `first()`, `last()`, `rows()`, `partition()`
2121
- [x] Collect data with filter and transform
2222

2323
Installation
@@ -369,6 +369,38 @@ var_dump(
369369
); // [1]
370370
```
371371

372+
#### 4. `Finder::partition()`
373+
374+
It splits data into two arrays: matching and non-matching items based on a filter.
375+
376+
```php
377+
use ArrayLookup\Finder;
378+
379+
// Basic partition - split numbers into greater than 5 and not
380+
$data = [1, 6, 3, 8, 4, 9];
381+
$filter = static fn($datum): bool => $datum > 5;
382+
383+
[$matching, $notMatching] = Finder::partition($data, $filter);
384+
385+
var_dump($matching); // [6, 8, 9]
386+
var_dump($notMatching); // [1, 3, 4]
387+
388+
// Partition with preserved keys
389+
[$matching, $notMatching] = Finder::partition($data, $filter, preserveKey: true);
390+
391+
var_dump($matching); // [1 => 6, 3 => 8, 5 => 9]
392+
var_dump($notMatching); // [0 => 1, 2 => 3, 4 => 4]
393+
394+
// Using the array key inside the filter
395+
$data = [10, 20, 30, 40];
396+
$keyFilter = static fn($datum, $key): bool => $key % 2 === 0;
397+
398+
[$even, $odd] = Finder::partition($data, $keyFilter, preserveKey: true);
399+
400+
var_dump($even); // [0 => 10, 2 => 30]
401+
var_dump($odd); // [1 => 20, 3 => 40]
402+
```
403+
372404
**D. Collector**
373405
---------------
374406

rector.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
declare(strict_types=1);
44

5+
use Rector\CodingStyle\Rector\FunctionLike\FunctionLikeToFirstClassCallableRector;
56
use Rector\Config\RectorConfig;
67
use Rector\DeadCode\Rector\FunctionLike\NarrowWideUnionReturnTypeRector;
78
use Rector\TypeDeclaration\Rector\ClassMethod\BoolReturnTypeFromBooleanStrictReturnsRector;
@@ -26,6 +27,9 @@
2627
NarrowWideUnionReturnTypeRector::class => [
2728
__DIR__ . '/tests/FilterTest.php',
2829
],
30+
FunctionLikeToFirstClassCallableRector::class => [
31+
__DIR__ . '/tests/FinderTest.php',
32+
],
2933
])
3034
->withParallel()
3135
->withRootFiles()

src/Finder.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,4 +165,45 @@ public static function rows(
165165

166166
return $rows;
167167
}
168+
169+
/**
170+
* Partitions data into two arrays: [matching, notMatching] based on filter.
171+
*
172+
* @param array<int|string, mixed>|Traversable<int|string, mixed> $data
173+
* @param callable(mixed $datum, int|string|null $key): bool $filter
174+
* @return array{0: array<int|string, mixed>, 1: array<int|string, mixed>}
175+
*/
176+
public static function partition(
177+
iterable $data,
178+
callable $filter,
179+
bool $preserveKey = false
180+
): array {
181+
// filter must be a callable with bool return type
182+
Filter::boolean($filter);
183+
184+
$matching = [];
185+
$notMatching = [];
186+
$matchKey = 0;
187+
$notMatchKey = 0;
188+
189+
foreach ($data as $key => $datum) {
190+
$isFound = $filter($datum, $key);
191+
192+
if ($isFound) {
193+
if ($preserveKey || ! is_numeric($key)) {
194+
$matching[$key] = $datum;
195+
} else {
196+
$matching[$matchKey] = $datum;
197+
++$matchKey;
198+
}
199+
} elseif ($preserveKey || ! is_numeric($key)) {
200+
$notMatching[$key] = $datum;
201+
} else {
202+
$notMatching[$notMatchKey] = $datum;
203+
++$notMatchKey;
204+
}
205+
}
206+
207+
return [$matching, $notMatching];
208+
}
168209
}

tests/FinderTest.php

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use stdClass;
1818

1919
use function current;
20+
use function is_string;
2021
use function str_contains;
2122

2223
final class FinderTest extends TestCase
@@ -382,4 +383,76 @@ public function testRowsWithLimit(): void
382383
Finder::rows($data, $filter, limit: 1)
383384
);
384385
}
386+
387+
/**
388+
* @param array<int|string, mixed> $expectedMatching
389+
* @param array<int|string, mixed> $expectedNotMatching
390+
* @param array<int|string, int|string> $data
391+
*/
392+
#[DataProvider('partitionDataProvider')]
393+
public function testPartition(
394+
iterable $data,
395+
callable $filter,
396+
array $expectedMatching,
397+
array $expectedNotMatching,
398+
bool $preserveKey = false
399+
): void {
400+
[$matching, $notMatching] = Finder::partition($data, $filter, $preserveKey);
401+
402+
$this->assertSame($expectedMatching, $matching);
403+
$this->assertSame($expectedNotMatching, $notMatching);
404+
}
405+
406+
/**
407+
* @return Iterator<string, array{iterable, callable, array<int|string, mixed>, array<int|string, mixed>, bool}>
408+
*/
409+
public static function partitionDataProvider(): Iterator
410+
{
411+
yield 'partition numbers greater than 5' => [
412+
[1, 6, 3, 8, 4, 9],
413+
static fn($datum): bool => $datum > 5,
414+
[6, 8, 9],
415+
[1, 3, 4],
416+
false,
417+
];
418+
419+
yield 'partition numbers with preserved keys' => [
420+
[1, 6, 3, 8, 4, 9],
421+
static fn($datum): bool => $datum > 5,
422+
[1 => 6, 3 => 8, 5 => 9],
423+
[0 => 1, 2 => 3, 4 => 4],
424+
true,
425+
];
426+
427+
yield 'partition using key in filter' => [
428+
[10, 20, 30, 40],
429+
static fn($datum, $key): bool => $key % 2 === 0,
430+
[0 => 10, 2 => 30],
431+
[1 => 20, 3 => 40],
432+
true,
433+
];
434+
435+
yield 'partition associative array' => [
436+
['name' => 'John', 'age' => 25, 'city' => 'NYC', 'score' => 100],
437+
static fn($datum): bool => is_string($datum),
438+
['name' => 'John', 'city' => 'NYC'],
439+
['age' => 25, 'score' => 100],
440+
false,
441+
];
442+
}
443+
444+
public function testPartitionPreservesOriginalData(): void
445+
{
446+
$data = [1, 2, 3];
447+
$copy = $data;
448+
449+
[$matching, $notMatching] = Finder::partition(
450+
$data,
451+
static fn($datum): bool => $datum > 1
452+
);
453+
454+
$this->assertSame([2, 3], $matching);
455+
$this->assertSame([1], $notMatching);
456+
$this->assertSame($copy, $data);
457+
}
385458
}

0 commit comments

Comments
 (0)