Skip to content

Commit 269a72e

Browse files
committed
Add Finder::partition() to splits data into 2 arrays: matching and non-matching
1 parent bb81037 commit 269a72e

File tree

4 files changed

+150
-1
lines changed

4 files changed

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

0 commit comments

Comments
 (0)