I\'im trying to find all combinations of items in several arrays. The number of arrays is random (this can be 2, 3, 4, 5...). The number of elements in each array is random
Here is a code that generates unique combinations from a set of numbers.
If you have a list of numbers, like 1,3,4,7,12
you can generate sets of X
numbers, all unique, no repetitive.
The first function works in PHP 7.4
or higher, and the second one uses keys to store the values. Both work very well based on the benchmark.
function get_combos74($map, $size, &$generated = [], $loop = 1, $i = 0, $prefix = [])
{
if ($loop == 1) {
sort($map);
}
for (; $i < count($map); $i++) {
if ($loop < $size) {
get_combos74($map, $size, $generated, $loop + 1, $i + 1, [...$prefix, $map[$i]]);
} else {
$generated[] = [...$prefix, $map[$i]];
}
}
return $generated;
}
function get_combosSTR($map, $size, &$generated = [], $loop = 1, $i = 0, $prefix = '')
{
if ($loop == 1) {
sort($map);
}
for (; $i < count($map); $i++) {
if ($loop < $size) {
get_combosSTR($map, $size, $generated, $loop + 1, $i + 1, "$prefix{$map[$i]}:");
} else {
$generated["$prefix{$map[$i]}"] = 0;
}
}
return $generated;
}
This is a cartesian product, and I just asked the same question not too long ago. Here is the algorithm that is posted on the PHP website.
function array_cartesian_product($arrays)
{
$result = array();
$arrays = array_values($arrays);
$sizeIn = sizeof($arrays);
$size = $sizeIn > 0 ? 1 : 0;
foreach ($arrays as $array)
$size = $size * sizeof($array);
for ($i = 0; $i < $size; $i ++)
{
$result[$i] = array();
for ($j = 0; $j < $sizeIn; $j ++)
array_push($result[$i], current($arrays[$j]));
for ($j = ($sizeIn -1); $j >= 0; $j --)
{
if (next($arrays[$j]))
break;
elseif (isset ($arrays[$j]))
reset($arrays[$j]);
}
}
return $result;
}
This code besides simplicity, get all combinations of multiple arrays and preserves keys.
function get_combinations($arrays) {
$result = array(array());
foreach ($arrays as $property => $property_values) {
$tmp = array();
foreach ($result as $result_item) {
foreach ($property_values as $property_key => $property_value) {
$tmp[] = $result_item + array($property_key => $property_value);
}
}
$result = $tmp;
}
return $result;
}
Exemple:
Array
(
Array
(
'1' => 'White',
'2' => 'Green',
'3' => 'Blue'
),
Array
(
'4' =>' Small',
'5' => 'Big'
)
)
Will return:
Array
(
[0] => Array
(
[1] => White
[4] => Small
)
[1] => Array
(
[1] => White
[5] => Big
)
[2] => Array
(
[2] => Green
[4] => Small
)
[3] => Array
(
[2] => Green
[5] => Big
)
[4] => Array
(
[3] => Blue
[4] => Small
)
[5] => Array
(
[3] => Blue
[5] => Big
)
)
Here is recursive solution:
function combinations($arrays, $i = 0) {
if (!isset($arrays[$i])) {
return array();
}
if ($i == count($arrays) - 1) {
return $arrays[$i];
}
// get combinations from subsequent arrays
$tmp = combinations($arrays, $i + 1);
$result = array();
// concat each array from tmp with each element from $arrays[$i]
foreach ($arrays[$i] as $v) {
foreach ($tmp as $t) {
$result[] = is_array($t) ?
array_merge(array($v), $t) :
array($v, $t);
}
}
return $result;
}
print_r(
combinations(
array(
array('A1','A2','A3'),
array('B1','B2','B3'),
array('C1','C2')
)
)
);
I know this question is old, but I got the same issue today and decided to give the new Generator a try:
function generateCombinations(array $array) {
foreach (array_pop($array) as $value) {
if (count($array)) {
foreach (generateCombinations($array) as $combination) {
yield array_merge([$value], $combination);
};
} else {
yield [$value];
}
}
}
foreach (generateCombinations(['a' => ['A'], 'b' => ['B'], 'c' => ['C', 'D'], 'd' => ['E', 'F', 'G']]) as $c) {
var_dump($c);
}
Result:
array(4) {
[0]=>
string(1) "E"
[1]=>
string(1) "C"
[2]=>
string(1) "B"
[3]=>
string(1) "A"
}
array(4) {
[0]=>
string(1) "E"
[1]=>
string(1) "D"
[2]=>
string(1) "B"
[3]=>
string(1) "A"
}
array(4) {
[0]=>
string(1) "F"
[1]=>
string(1) "C"
[2]=>
string(1) "B"
[3]=>
string(1) "A"
}
array(4) {
[0]=>
string(1) "F"
[1]=>
string(1) "D"
[2]=>
string(1) "B"
[3]=>
string(1) "A"
}
array(4) {
[0]=>
string(1) "G"
[1]=>
string(1) "C"
[2]=>
string(1) "B"
[3]=>
string(1) "A"
}
array(4) {
[0]=>
string(1) "G"
[1]=>
string(1) "D"
[2]=>
string(1) "B"
[3]=>
string(1) "A"
}
I like this solution: https://stackoverflow.com/a/33259643/3163536 but to answer the actual question (which assumes that the number of elements of each combination should be equal to the number of incoming arrays) the function should be modified:
function getCombinations(...$arrays)
{
$result = [[]];
foreach ($arrays as $property => $property_values) {
$tmp = [];
foreach ($result as $result_item) {
foreach ($property_values as $property_value) {
$tmp[] = array_merge($result_item, [$property => $property_value]);
}
}
$result = $tmp;
}
return $result;
}
The usage:
$arrayA = array('A1','A2','A3');
$arrayB = array('B1','B2','B3');
$arrayC = array('C1','C2');
print_r(getCombinations($arrayA, $arrayB, $arrayC));
The result:
Array
(
[0] => Array
(
[0] => A1
[1] => B1
[2] => C1
)
[1] => Array
(
[0] => A1
[1] => B1
[2] => C2
)
[2] => Array
(
[0] => A1
[1] => B2
[2] => C1
)
[3] => Array
(
[0] => A1
[1] => B2
[2] => C2
)
[4] => Array
(
[0] => A1
[1] => B3
[2] => C1
)
[5] => Array
(
[0] => A1
[1] => B3
[2] => C2
)
[6] => Array
(
[0] => A2
[1] => B1
[2] => C1
)
[7] => Array
(
[0] => A2
[1] => B1
[2] => C2
)
[8] => Array
(
[0] => A2
[1] => B2
[2] => C1
)
[9] => Array
(
[0] => A2
[1] => B2
[2] => C2
)
[10] => Array
(
[0] => A2
[1] => B3
[2] => C1
)
[11] => Array
(
[0] => A2
[1] => B3
[2] => C2
)
[12] => Array
(
[0] => A3
[1] => B1
[2] => C1
)
[13] => Array
(
[0] => A3
[1] => B1
[2] => C2
)
[14] => Array
(
[0] => A3
[1] => B2
[2] => C1
)
[15] => Array
(
[0] => A3
[1] => B2
[2] => C2
)
[16] => Array
(
[0] => A3
[1] => B3
[2] => C1
)
[17] => Array
(
[0] => A3
[1] => B3
[2] => C2
)
)