Skip to content

Commit 2a3442d

Browse files
authored
Feat: 2.0 (#13)
1 parent e1f7c26 commit 2a3442d

8 files changed

Lines changed: 476 additions & 403 deletions

File tree

.github/workflows/ci-workflow.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,6 @@ jobs:
3737
max-parallel: 10
3838
matrix:
3939
php:
40-
- 7.4
41-
- 8.0
42-
- 8.1
4340
- 8.2
4441
- 8.3
4542
- 8.4
@@ -60,7 +57,7 @@ jobs:
6057
run: composer install --prefer-dist --no-progress --ignore-platform-req=php
6158

6259
- name: Run tests
63-
run: vendor/bin/phpunit --coverage-clover=coverage.xml
60+
run: vendor/bin/pest --coverage-clover=coverage.xml
6461

6562
- name: Upload coverage to Codecov
6663
uses: codecov/codecov-action@v5

README.md

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ Usage
1212
-------
1313

1414
```php
15-
require_once __DIR__ . '/vendor/autoload.php';
16-
1715
use function BenTools\CartesianProduct\combinations;
1816

1917
$data = [
@@ -103,15 +101,12 @@ Array
103101
)
104102
```
105103

106-
107104
Combinations count
108105
------------------
109106

110-
You can simply count how many combinations your data produce:
107+
You can simply count how many combinations your data produce (this will not generate any combination):
111108

112109
```php
113-
require_once __DIR__ . '/vendor/autoload.php';
114-
115110
use function BenTools\CartesianProduct\combinations;
116111

117112
$data = [
@@ -132,11 +127,54 @@ $data = [
132127
var_dump(count(combinations($data))); // 2 * 3 * 2 = 12
133128
```
134129

130+
Filtering combinations
131+
----------------------
132+
133+
You can filter combinations using the `filter` method. This is useful if you want to skip some combinations based on certain criteria:
134+
135+
```php
136+
use function BenTools\CartesianProduct\combinations;
137+
138+
$data = [
139+
'hair' => [
140+
'blond',
141+
'black'
142+
],
143+
'eyes' => [
144+
'blue',
145+
'green',
146+
]
147+
];
148+
149+
foreach (combinations($data)->filter(fn (array $combination) => 'green' !== $combination['eyes']) as $combination) {
150+
printf('Hair: %s - Eyes: %s' . PHP_EOL, $combination['hair'], $combination['eyes']);
151+
}
152+
```
153+
154+
Map output
155+
----------
156+
157+
You can use the `each` method to transform each combination into a different format:
158+
159+
```php
160+
use App\Entity\Book;
161+
162+
use function BenTools\CartesianProduct\combinations;
163+
164+
$books = [
165+
'author' => ['Isaac Asimov', 'Arthur C. Clarke'],
166+
'genre' => ['Science Fiction', 'Fantasy'],
167+
]
168+
169+
foreach (combinations($books)->each(fn (array $combination) => Book::fromArray($combination)) as $book) {
170+
assert($book instanceof Book);
171+
}
172+
```
135173

136174
Installation
137175
------------
138176

139-
PHP 7.4+ is required.
177+
PHP 8.2+ is required.
140178
```
141179
composer require bentools/cartesian-product
142180
```
@@ -146,7 +184,6 @@ Performance test
146184
The following example was executed on my Core i7 personnal computer with 8GB RAM.
147185

148186
```php
149-
require_once __DIR__ . '/vendor/autoload.php';
150187
use function BenTools\CartesianProduct\combinations;
151188

152189
$data = array_fill(0, 10, array_fill(0, 5, 'foo'));
@@ -172,7 +209,7 @@ Output:
172209
Unit tests
173210
----------
174211
```
175-
./vendor/bin/phpunit
212+
./vendor/bin/pest
176213
```
177214

178215

composer.json

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,17 @@
1818
]
1919
},
2020
"require": {
21-
"php": ">=7.4"
21+
"php": "^8.2"
2222
},
2323
"require-dev": {
2424
"symfony/var-dumper": "^6.0|^7.0",
25-
"phpunit/phpunit": "^9.0",
26-
"friendsofphp/php-cs-fixer": "^3.82"
25+
"friendsofphp/php-cs-fixer": "^3.82",
26+
"pestphp/pest": "^3.8",
27+
"pestphp/pest-plugin-drift": "^3.0"
28+
},
29+
"config": {
30+
"allow-plugins": {
31+
"pestphp/pest-plugin": true
32+
}
2733
}
2834
}

phpunit.xml.dist

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" backupStaticAttributes="false"
3-
beStrictAboutTestsThatDoNotTestAnything="true" beStrictAboutOutputDuringTests="true"
4-
bootstrap="vendor/autoload.php" colors="true" convertErrorsToExceptions="true"
5-
convertNoticesToExceptions="true" convertWarningsToExceptions="true" failOnRisky="true" failOnWarning="true"
6-
processIsolation="false" stopOnError="false" stopOnFailure="false" verbose="true" convertDeprecationsToExceptions="false"
7-
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
8-
<coverage processUncoveredFiles="true">
9-
<include>
10-
<directory suffix=".php">./src</directory>
11-
</include>
12-
</coverage>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
4+
bootstrap="vendor/autoload.php"
5+
colors="true"
6+
>
137
<testsuites>
14-
<testsuite name="New function">
15-
<file>tests/TestCombinationsFunction.php</file>
16-
</testsuite>
17-
<testsuite name="Legacy function">
18-
<file>tests/TestCartesianProductFunction.php</file>
8+
<testsuite name="Test Suite">
9+
<directory suffix="Test.php">./tests</directory>
1910
</testsuite>
2011
</testsuites>
12+
<source>
13+
<include>
14+
<directory>src</directory>
15+
</include>
16+
</source>
2117
</phpunit>

src/CartesianProduct.php

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
use function iterator_to_array;
1919

2020
/**
21-
* @internal - use the function `cartesian_product()` instead.
21+
* @internal - use the function `combinations()` instead.
2222
* @template TKey
2323
* @template TValue
2424
* @implements IteratorAggregate<array<TKey, TValue>>
@@ -31,6 +31,8 @@ final class CartesianProduct implements IteratorAggregate, Countable
3131
private array $set;
3232
private bool $isRecursiveStep = false;
3333
private int $count;
34+
private ?Closure $filterCallback = null;
35+
private ?Closure $mapCallback = null;
3436

3537
/**
3638
* @param array<TKey, iterable<TValue>> $set
@@ -40,6 +42,22 @@ public function __construct(array $set)
4042
$this->set = $set;
4143
}
4244

45+
public function filter(callable $callback): self
46+
{
47+
$clone = clone $this;
48+
$clone->filterCallback = Closure::fromCallable($callback);
49+
50+
return $clone;
51+
}
52+
53+
public function each(callable $callback): self
54+
{
55+
$clone = clone $this;
56+
$clone->mapCallback = Closure::fromCallable($callback);
57+
58+
return $clone;
59+
}
60+
4361
/**
4462
* @return Traversable<array<TKey, TValue>>
4563
*/
@@ -60,7 +78,11 @@ public function getIterator(): Traversable
6078
$this->validate($subset, $last);
6179
foreach (self::subset($set) as $product) {
6280
foreach ($subset as $value) {
63-
yield $product + [$last => ($value instanceof Closure ? $value($product) : $value)];
81+
$combination = $product + [$last => ($value instanceof Closure ? $value($product) : $value)];
82+
if ($this->filterCallback && !($this->filterCallback)($combination)) {
83+
continue;
84+
}
85+
yield $this->mapCallback ? ($this->mapCallback)($combination) : $combination;
6486
}
6587
}
6688
}
@@ -91,7 +113,7 @@ function ($subset, $key) {
91113
* @param mixed $subset
92114
* @param TKey $key
93115
*/
94-
private function validate($subset, $key): void
116+
private function validate(mixed $subset, $key): void
95117
{
96118
// Validate array subset
97119
if (is_array($subset) && !empty($subset)) {

src/function.php

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

33
namespace BenTools\CartesianProduct;
44

5-
use function trigger_error;
6-
7-
use const E_USER_DEPRECATED;
8-
9-
require_once __DIR__ . '/CartesianProduct.php';
10-
11-
/**
12-
* @template TKey
13-
* @template TValue
14-
* @param array<TKey, iterable<TValue>> $set - A multidimensionnal array.
15-
* @return CartesianProduct<TKey, TValue>
16-
*/
17-
function cartesian_product(array $set): CartesianProduct
18-
{
19-
trigger_error(sprintf(
20-
'The function %s() is deprecated since 1.5 and will be removed in 2.0. Use %s() instead.',
21-
__FUNCTION__,
22-
'BenTools\CartesianProduct\combinations'
23-
), E_USER_DEPRECATED);
24-
25-
return new CartesianProduct($set);
26-
}
27-
285
/**
296
* @template TKey
307
* @template TValue

0 commit comments

Comments
 (0)