, (*1)
Cartesian Product
A simple, low-memory footprint function to generate all combinations from a multi-dimensionnal array., (*2)
Usage
use function BenTools\CartesianProduct\combinations;
$data = [
'hair' => [
'blond',
'black'
],
'eyes' => [
'blue',
'green',
function (array $combination) { // You can use closures to dynamically generate possibilities
if ('black' === $combination['hair']) { // Then you have access to the current combination being built
return 'brown';
}
return 'grey';
}
]
];
foreach (combinations($data) as $combination) {
printf('Hair: %s - Eyes: %s' . PHP_EOL, $combination['hair'], $combination['eyes']);
}
Output:, (*3)
Hair: blond - Eyes: blue
Hair: blond - Eyes: green
Hair: blond - Eyes: grey
Hair: black - Eyes: blue
Hair: black - Eyes: green
Hair: black - Eyes: brown
Array output
Instead of using foreach
you can dump all possibilities into an array., (*4)
[!WARNING]
This will dump all combinations in memory, so be careful with large datasets., (*5)
print_r(combinations($data)->asArray());
Output:, (*6)
Array
(
[0] => Array
(
[hair] => blond
[eyes] => blue
)
[1] => Array
(
[hair] => blond
[eyes] => green
)
[2] => Array
(
[hair] => blond
[eyes] => grey
)
[3] => Array
(
[hair] => black
[eyes] => blue
)
[4] => Array
(
[hair] => black
[eyes] => green
)
[5] => Array
(
[hair] => black
[eyes] => brown
)
)
Combinations count
You can simply count how many combinations your data produce (this will not generate any combination):, (*7)
use function BenTools\CartesianProduct\combinations;
$data = [
'hair' => [
'blond',
'red',
],
'eyes' => [
'blue',
'green',
'brown',
],
'gender' => [
'male',
'female',
]
];
var_dump(count(combinations($data))); // 2 * 3 * 2 = 12
Filtering combinations
You can filter combinations using the filter
method. This is useful if you want to skip some combinations based on certain criteria:, (*8)
use function BenTools\CartesianProduct\combinations;
$data = [
'hair' => [
'blond',
'black'
],
'eyes' => [
'blue',
'green',
]
];
foreach (combinations($data)->filter(fn (array $combination) => 'green' !== $combination['eyes']) as $combination) {
printf('Hair: %s - Eyes: %s' . PHP_EOL, $combination['hair'], $combination['eyes']);
}
Map output
You can use the each
method to transform each combination into a different format:, (*9)
use App\Entity\Book;
use function BenTools\CartesianProduct\combinations;
$books = [
'author' => ['Isaac Asimov', 'Arthur C. Clarke'],
'genre' => ['Science Fiction', 'Fantasy'],
]
foreach (combinations($books)->each(fn (array $combination) => Book::fromArray($combination)) as $book) {
assert($book instanceof Book);
}
Installation
PHP 8.2+ is required., (*10)
composer require bentools/cartesian-product
The following example was executed on my Core i7 personnal computer with 8GB RAM., (*11)
use function BenTools\CartesianProduct\combinations;
$data = array_fill(0, 10, array_fill(0, 5, 'foo'));
$start = microtime(true);
foreach (combinations($data) as $c => $combination) {
continue;
}
$end = microtime(true);
printf(
'Generated %d combinations in %ss - Memory usage: %sMB / Peak usage: %sMB',
++$c,
round($end - $start, 3),
round(memory_get_usage() / 1024 / 1024),
round(memory_get_peak_usage() / 1024 / 1024)
);
Output:, (*12)
Generated 9765625 combinations in 1.61s - Memory usage: 0MB / Peak usage: 1MB, (*13)
Unit tests
./vendor/bin/pest
Other implementations
th3n3rd/cartesian-product, (*14)
patchranger/cartesian-iterator, (*15)
Benchmark, (*16)
See also
bentools/string-combinations, (*17)
bentools/iterable-functions, (*18)
Credits
Titus on StackOverflow - you really rock., (*19)