Finding Cartesian Product With PHP Associative Arrays

Finding cartesian product with PHP associative arrays

Here's a solution I wouldn't be ashamed to show.

Rationale

Assume that we have an input array $input with N sub-arrays, as in your example. Each
sub-array has Cn items, where n is its index inside $input, and its key is Kn. I will refer to the ith item of the nth sub-array as Vn,i.

The algorithm below can be proved to work (barring bugs) by induction:

1) For N = 1, the cartesian product is simply array(0 => array(K1 => V1,1), 1 => array(K1 => V1,2), ... ) -- C1 items in total. This can be done with a simple foreach.

2) Assume that $result already holds the cartesian product of the first N-1 sub-arrays. The cartesian product of $result and the Nth sub-array can be produced this way:

3) In each item (array) inside $product, add the value KN => VN,1. Remember the resulting item (with the added value); I 'll refer to it as $item.

4a) For each array inside $product:

4b) For each value in the set VN,2 ... VN,CN, add to $product a copy of $item, but change the value with the key KN to VN,m (for all 2 <= m <= CN).

The two iterations 4a (over $product) and 4b (over the Nth input sub-array) ends up with $result having CN items for every item it had before the iterations, so in the end $result indeed contains the cartesian product of the first N sub arrays.

Therefore the algorithm will work for any N.

This was harder to write than it should have been. My formal proofs are definitely getting rusty...

Code

function cartesian($input) {
$result = array();

while (list($key, $values) = each($input)) {
// If a sub-array is empty, it doesn't affect the cartesian product
if (empty($values)) {
continue;
}

// Seeding the product array with the values from the first sub-array
if (empty($result)) {
foreach($values as $value) {
$result[] = array($key => $value);
}
}
else {
// Second and subsequent input sub-arrays work like this:
// 1. In each existing array inside $product, add an item with
// key == $key and value == first item in input sub-array
// 2. Then, for each remaining item in current input sub-array,
// add a copy of each existing array inside $product with
// key == $key and value == first item of input sub-array

// Store all items to be added to $product here; adding them
// inside the foreach will result in an infinite loop
$append = array();

foreach($result as &$product) {
// Do step 1 above. array_shift is not the most efficient, but
// it allows us to iterate over the rest of the items with a
// simple foreach, making the code short and easy to read.
$product[$key] = array_shift($values);

// $product is by reference (that's why the key we added above
// will appear in the end result), so make a copy of it here
$copy = $product;

// Do step 2 above.
foreach($values as $item) {
$copy[$key] = $item;
$append[] = $copy;
}

// Undo the side effecst of array_shift
array_unshift($values, $product[$key]);
}

// Out of the foreach, we can add to $results now
$result = array_merge($result, $append);
}
}

return $result;
}

Usage

$input = array(
'arm' => array('A', 'B', 'C'),
'gender' => array('Female', 'Male'),
'location' => array('Vancouver', 'Calgary'),
);

print_r(cartesian($input));

cartesian product with PHP (id, name, variants)

That is actually working code for now but don't know how much is efficient.

// filter out properties without options
$withOptions = array_filter($properties, function($property) {
return count($property['options']) > 0;
});

$result = [];

$skipFirst = true;

foreach ($withOptions as $property) {

if ($skipFirst) {

foreach (reset($withOptions)['options'] as $id => $option) {
$result[$id] = $option['name'];
}

$skipFirst = false;
continue;
}

foreach ($result as $code => $variant) {
foreach ($property['options'] as $id => $option) {
$new = $code . "-" . $id;
$result[$new] = $variant . " / " . $option['name'];
unset($result[$code]);
}
}
}

Cartesian product of associative array showing key and value - PHP

I think I got it, I googled "PHP combinations" (Assuming that a combination is similar to a Cartesian product, I think it is :s) and used this code as a base. The change I needed to make was instead of merging the arrays I had to use a union because according to the php manual:

Values in the input array with numeric keys will be renumbered with incrementing keys starting from zero in the result array.

and

If you want to append array elements from the second array to the first array while not overwriting the elements from the first array and not re-indexing, use the + array union operator. ... The keys from the first array will be preserved. If an array key exists in both arrays, then the element from the first array will be used and the matching key's element from the second array will be ignored.

CODE:

<?php
$temp = Array
(
"0" => Array
(
"11134" => 3.430,
"11131" => 2.720,
"11128" => 1.077
),

"1" => Array
(
"11135" => 2.381,
"11132" => 2.636,
"11129" => 2.920
),

"2" => Array
(
"11136" => 1.220,
"11133" => 2.550,
"11130" => 3.895
)

);


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;
}

$combinations = get_combinations($temp);

var_dump($combinations);
?>

RESULTS:

array(27) {
[0]=>
array(3) {
[11134]=>
float(3.43)
[11135]=>
float(2.381)
[11136]=>
float(1.22)
}
[1]=>
array(3) {
[11134]=>
float(3.43)
[11135]=>
float(2.381)
[11133]=>
float(2.55)
}
[2]=>
array(3) {
[11134]=>
float(3.43)
[11135]=>
float(2.381)
[11130]=>
float(3.895)
}
[3]=>
array(3) {
[11134]=>
float(3.43)
[11132]=>
float(2.636)
[11136]=>
float(1.22)
}
[4]=>
array(3) {
[11134]=>
float(3.43)
[11132]=>
float(2.636)
[11133]=>
float(2.55)
}
[5]=>
array(3) {
[11134]=>
float(3.43)
[11132]=>
float(2.636)
[11130]=>
float(3.895)
}
[6]=>
array(3) {
[11134]=>
float(3.43)
[11129]=>
float(2.92)
[11136]=>
float(1.22)
}
[7]=>
array(3) {
[11134]=>
float(3.43)
[11129]=>
float(2.92)
[11133]=>
float(2.55)
}
[8]=>
array(3) {
[11134]=>
float(3.43)
[11129]=>
float(2.92)
[11130]=>
float(3.895)
}
[9]=>
array(3) {
[11131]=>
float(2.72)
[11135]=>
float(2.381)
[11136]=>
float(1.22)
}
[10]=>
array(3) {
[11131]=>
float(2.72)
[11135]=>
float(2.381)
[11133]=>
float(2.55)
}
[11]=>
array(3) {
[11131]=>
float(2.72)
[11135]=>
float(2.381)
[11130]=>
float(3.895)
}
[12]=>
array(3) {
[11131]=>
float(2.72)
[11132]=>
float(2.636)
[11136]=>
float(1.22)
}
[13]=>
array(3) {
[11131]=>
float(2.72)
[11132]=>
float(2.636)
[11133]=>
float(2.55)
}
[14]=>
array(3) {
[11131]=>
float(2.72)
[11132]=>
float(2.636)
[11130]=>
float(3.895)
}
[15]=>
array(3) {
[11131]=>
float(2.72)
[11129]=>
float(2.92)
[11136]=>
float(1.22)
}
[16]=>
array(3) {
[11131]=>
float(2.72)
[11129]=>
float(2.92)
[11133]=>
float(2.55)
}
[17]=>
array(3) {
[11131]=>
float(2.72)
[11129]=>
float(2.92)
[11130]=>
float(3.895)
}
[18]=>
array(3) {
[11128]=>
float(1.077)
[11135]=>
float(2.381)
[11136]=>
float(1.22)
}
[19]=>
array(3) {
[11128]=>
float(1.077)
[11135]=>
float(2.381)
[11133]=>
float(2.55)
}
[20]=>
array(3) {
[11128]=>
float(1.077)
[11135]=>
float(2.381)
[11130]=>
float(3.895)
}
[21]=>
array(3) {
[11128]=>
float(1.077)
[11132]=>
float(2.636)
[11136]=>
float(1.22)
}
[22]=>
array(3) {
[11128]=>
float(1.077)
[11132]=>
float(2.636)
[11133]=>
float(2.55)
}
[23]=>
array(3) {
[11128]=>
float(1.077)
[11132]=>
float(2.636)
[11130]=>
float(3.895)
}
[24]=>
array(3) {
[11128]=>
float(1.077)
[11129]=>
float(2.92)
[11136]=>
float(1.22)
}
[25]=>
array(3) {
[11128]=>
float(1.077)
[11129]=>
float(2.92)
[11133]=>
float(2.55)
}
[26]=>
array(3) {
[11128]=>
float(1.077)
[11129]=>
float(2.92)
[11130]=>
float(3.895)
}
}

Get all unique array combinations using Cartesian Product PHP

Here's a generic cross product solution (not mine, I found it somewhere, don't remember where but there's a bunch available online):

function crossProduct() {
$_ = func_get_args();
if (count($_) == 0) {
return array(array());
}
$a = array_shift($_);
$c = call_user_func_array('crossProduct', $_);
$r = array();
foreach ($a as $v) {
foreach ($c as $p) {
$r[] = array_merge(array($v), $p);
}
}
return $r;
}

You will however need to pre-process your solution a bit:

$allDepartmentsSections = [];
foreach ($jsonArrayEntry as $entry) {
$sections = [];
foreach ($entry["section"] as $section) {
$sections[] = $section + [ "department" => $entry["department"], "name"=>$entry["name"] ];
}
$allDepartmentsSections[] = $sections;
}

You can then do:

call_user_func_array('crossProduct', $allDepartmentsSections);

See it working at https://3v4l.org/sSFpu

Best way to create combination of each element using PHP

You can use a recursive function to build the result array, for example:

function combine($arr) {
if (count($arr) === 1) {
return array_shift($arr);
}
$items = array_shift($arr);

$data = [];
foreach ($items as $item) {
foreach (combine($arr) as $value) {
$data[] = $item . ' ' . $value;
}
}
return $data;
}

and using example:

$items = [
[
'Red',
'Yellow',
'Blue',
'Green',
],
[
28,
30,
32,
34
],
[
'Short Neck',
'Full Neck',
'Round Neck'
],
[
'Bodycon',
'Empire Waist',
'High-Low'
]
];

$result = combine($items);

PHP Cartesian Product of two associative arrays while combining keys and labels

I ended up figuring it out. Not completely happy with my test of strpos() and my explode('_'...) but it works and does what I need.

http://ideone.com/1WDOp

<?php
function custombuttons_generate_children(&$options, $a, $m, $k) {
$mcount = count($m);
if (empty($options[$a[$k]])) {
foreach ($m as $key => $label) {
$new_key = implode('_', array($k, $key));
$new_label = implode('+', array($label, $a[$k]));
$options[$a[$k]][$new_key] = $new_label;
}
}
else {
foreach ($options[$a[$k]] as $key => $label) {
foreach ($m as $mkey => $mlabel) {
if (!strpos($key, '_' . $mkey)) {
$new_key = implode('_', array($key, $mkey));
$new_label = implode('+', array($mlabel, $label));
$options[$a[$k]][$new_key] = $new_label;
}
}
}
}
$last = end(array_keys($options[$a[$k]]));
if (count(explode('_', $last)) !== $mcount) {
custombuttons_generate_children($options, $a, $m, $k);
}
}

function custombuttons_action_options_list() {
$a = array(
'left' => 'LEFT',
'middle' => 'MIDDLE',
'right' => 'RIGHT',
);
$m = array( // modifiers
'ctrl' => 'CTRL',
'alt' => 'ALT',
'shift' => 'SHIFT',
'double' => 'DOUBLE',
);
foreach ($a as $key => $label) {
$options[$key] = $label;
$options[$label] = array();
custombuttons_generate_children($options, $a, $m, $key);
}
return $options;
}
$options = custombuttons_action_options_list();
?>

Output:

Array
(
[left] => LEFT
[LEFT] => Array
(
[left_ctrl] => CTRL+LEFT
[left_alt] => ALT+LEFT
[left_shift] => SHIFT+LEFT
[left_double] => DOUBLE+LEFT
[left_ctrl_alt] => ALT+CTRL+LEFT
[left_ctrl_shift] => SHIFT+CTRL+LEFT
[left_ctrl_double] => DOUBLE+CTRL+LEFT
[left_alt_ctrl] => CTRL+ALT+LEFT
[left_alt_shift] => SHIFT+ALT+LEFT
[left_alt_double] => DOUBLE+ALT+LEFT
[left_shift_ctrl] => CTRL+SHIFT+LEFT
[left_shift_alt] => ALT+SHIFT+LEFT
[left_shift_double] => DOUBLE+SHIFT+LEFT
[left_double_ctrl] => CTRL+DOUBLE+LEFT
[left_double_alt] => ALT+DOUBLE+LEFT
[left_double_shift] => SHIFT+DOUBLE+LEFT
[left_ctrl_alt_shift] => SHIFT+ALT+CTRL+LEFT
[left_ctrl_alt_double] => DOUBLE+ALT+CTRL+LEFT
[left_ctrl_shift_alt] => ALT+SHIFT+CTRL+LEFT
[left_ctrl_shift_double] => DOUBLE+SHIFT+CTRL+LEFT
[left_ctrl_double_alt] => ALT+DOUBLE+CTRL+LEFT
[left_ctrl_double_shift] => SHIFT+DOUBLE+CTRL+LEFT
[left_alt_ctrl_shift] => SHIFT+CTRL+ALT+LEFT
[left_alt_ctrl_double] => DOUBLE+CTRL+ALT+LEFT
[left_alt_shift_ctrl] => CTRL+SHIFT+ALT+LEFT
[left_alt_shift_double] => DOUBLE+SHIFT+ALT+LEFT
[left_alt_double_ctrl] => CTRL+DOUBLE+ALT+LEFT
[left_alt_double_shift] => SHIFT+DOUBLE+ALT+LEFT
[left_shift_ctrl_alt] => ALT+CTRL+SHIFT+LEFT
[left_shift_ctrl_double] => DOUBLE+CTRL+SHIFT+LEFT
[left_shift_alt_ctrl] => CTRL+ALT+SHIFT+LEFT
[left_shift_alt_double] => DOUBLE+ALT+SHIFT+LEFT
[left_shift_double_ctrl] => CTRL+DOUBLE+SHIFT+LEFT
[left_shift_double_alt] => ALT+DOUBLE+SHIFT+LEFT
[left_double_ctrl_alt] => ALT+CTRL+DOUBLE+LEFT
[left_double_ctrl_shift] => SHIFT+CTRL+DOUBLE+LEFT
[left_double_alt_ctrl] => CTRL+ALT+DOUBLE+LEFT
[left_double_alt_shift] => SHIFT+ALT+DOUBLE+LEFT
[left_double_shift_ctrl] => CTRL+SHIFT+DOUBLE+LEFT
[left_double_shift_alt] => ALT+SHIFT+DOUBLE+LEFT
)

[middle] => MIDDLE
[MIDDLE] => Array
(
[middle_ctrl] => CTRL+MIDDLE
[middle_alt] => ALT+MIDDLE
[middle_shift] => SHIFT+MIDDLE
[middle_double] => DOUBLE+MIDDLE
[middle_ctrl_alt] => ALT+CTRL+MIDDLE
[middle_ctrl_shift] => SHIFT+CTRL+MIDDLE
[middle_ctrl_double] => DOUBLE+CTRL+MIDDLE
[middle_alt_ctrl] => CTRL+ALT+MIDDLE
[middle_alt_shift] => SHIFT+ALT+MIDDLE
[middle_alt_double] => DOUBLE+ALT+MIDDLE
[middle_shift_ctrl] => CTRL+SHIFT+MIDDLE
[middle_shift_alt] => ALT+SHIFT+MIDDLE
[middle_shift_double] => DOUBLE+SHIFT+MIDDLE
[middle_double_ctrl] => CTRL+DOUBLE+MIDDLE
[middle_double_alt] => ALT+DOUBLE+MIDDLE
[middle_double_shift] => SHIFT+DOUBLE+MIDDLE
[middle_ctrl_alt_shift] => SHIFT+ALT+CTRL+MIDDLE
[middle_ctrl_alt_double] => DOUBLE+ALT+CTRL+MIDDLE
[middle_ctrl_shift_alt] => ALT+SHIFT+CTRL+MIDDLE
[middle_ctrl_shift_double] => DOUBLE+SHIFT+CTRL+MIDDLE
[middle_ctrl_double_alt] => ALT+DOUBLE+CTRL+MIDDLE
[middle_ctrl_double_shift] => SHIFT+DOUBLE+CTRL+MIDDLE
[middle_alt_ctrl_shift] => SHIFT+CTRL+ALT+MIDDLE
[middle_alt_ctrl_double] => DOUBLE+CTRL+ALT+MIDDLE
[middle_alt_shift_ctrl] => CTRL+SHIFT+ALT+MIDDLE
[middle_alt_shift_double] => DOUBLE+SHIFT+ALT+MIDDLE
[middle_alt_double_ctrl] => CTRL+DOUBLE+ALT+MIDDLE
[middle_alt_double_shift] => SHIFT+DOUBLE+ALT+MIDDLE
[middle_shift_ctrl_alt] => ALT+CTRL+SHIFT+MIDDLE
[middle_shift_ctrl_double] => DOUBLE+CTRL+SHIFT+MIDDLE
[middle_shift_alt_ctrl] => CTRL+ALT+SHIFT+MIDDLE
[middle_shift_alt_double] => DOUBLE+ALT+SHIFT+MIDDLE
[middle_shift_double_ctrl] => CTRL+DOUBLE+SHIFT+MIDDLE
[middle_shift_double_alt] => ALT+DOUBLE+SHIFT+MIDDLE
[middle_double_ctrl_alt] => ALT+CTRL+DOUBLE+MIDDLE
[middle_double_ctrl_shift] => SHIFT+CTRL+DOUBLE+MIDDLE
[middle_double_alt_ctrl] => CTRL+ALT+DOUBLE+MIDDLE
[middle_double_alt_shift] => SHIFT+ALT+DOUBLE+MIDDLE
[middle_double_shift_ctrl] => CTRL+SHIFT+DOUBLE+MIDDLE
[middle_double_shift_alt] => ALT+SHIFT+DOUBLE+MIDDLE
)

[right] => RIGHT
[RIGHT] => Array
(
[right_ctrl] => CTRL+RIGHT
[right_alt] => ALT+RIGHT
[right_shift] => SHIFT+RIGHT
[right_double] => DOUBLE+RIGHT
[right_ctrl_alt] => ALT+CTRL+RIGHT
[right_ctrl_shift] => SHIFT+CTRL+RIGHT
[right_ctrl_double] => DOUBLE+CTRL+RIGHT
[right_alt_ctrl] => CTRL+ALT+RIGHT
[right_alt_shift] => SHIFT+ALT+RIGHT
[right_alt_double] => DOUBLE+ALT+RIGHT
[right_shift_ctrl] => CTRL+SHIFT+RIGHT
[right_shift_alt] => ALT+SHIFT+RIGHT
[right_shift_double] => DOUBLE+SHIFT+RIGHT
[right_double_ctrl] => CTRL+DOUBLE+RIGHT
[right_double_alt] => ALT+DOUBLE+RIGHT
[right_double_shift] => SHIFT+DOUBLE+RIGHT
[right_ctrl_alt_shift] => SHIFT+ALT+CTRL+RIGHT
[right_ctrl_alt_double] => DOUBLE+ALT+CTRL+RIGHT
[right_ctrl_shift_alt] => ALT+SHIFT+CTRL+RIGHT
[right_ctrl_shift_double] => DOUBLE+SHIFT+CTRL+RIGHT
[right_ctrl_double_alt] => ALT+DOUBLE+CTRL+RIGHT
[right_ctrl_double_shift] => SHIFT+DOUBLE+CTRL+RIGHT
[right_alt_ctrl_shift] => SHIFT+CTRL+ALT+RIGHT
[right_alt_ctrl_double] => DOUBLE+CTRL+ALT+RIGHT
[right_alt_shift_ctrl] => CTRL+SHIFT+ALT+RIGHT
[right_alt_shift_double] => DOUBLE+SHIFT+ALT+RIGHT
[right_alt_double_ctrl] => CTRL+DOUBLE+ALT+RIGHT
[right_alt_double_shift] => SHIFT+DOUBLE+ALT+RIGHT
[right_shift_ctrl_alt] => ALT+CTRL+SHIFT+RIGHT
[right_shift_ctrl_double] => DOUBLE+CTRL+SHIFT+RIGHT
[right_shift_alt_ctrl] => CTRL+ALT+SHIFT+RIGHT
[right_shift_alt_double] => DOUBLE+ALT+SHIFT+RIGHT
[right_shift_double_ctrl] => CTRL+DOUBLE+SHIFT+RIGHT
[right_shift_double_alt] => ALT+DOUBLE+SHIFT+RIGHT
[right_double_ctrl_alt] => ALT+CTRL+DOUBLE+RIGHT
[right_double_ctrl_shift] => SHIFT+CTRL+DOUBLE+RIGHT
[right_double_alt_ctrl] => CTRL+ALT+DOUBLE+RIGHT
[right_double_alt_shift] => SHIFT+ALT+DOUBLE+RIGHT
[right_double_shift_ctrl] => CTRL+SHIFT+DOUBLE+RIGHT
[right_double_shift_alt] => ALT+SHIFT+DOUBLE+RIGHT
)

)

I reorderd the keys/labels slightly but that didn't really change the solution.

Cartesian product of PHP arrays

Use readymade Cartesian class .

Use it like,

$output = Cartesian::build($start);

Author: jwage

Cartesian Product of N arrays

this is called "cartesian product", php man page on arrays http://php.net/manual/en/ref.array.php shows some implementations (in comments).

and here's yet another one:

function array_cartesian() {
$_ = func_get_args();
if(count($_) == 0)
return array(array());
$a = array_shift($_);
$c = call_user_func_array(__FUNCTION__, $_);
$r = array();
foreach($a as $v)
foreach($c as $p)
$r[] = array_merge(array($v), $p);
return $r;
}

$cross = array_cartesian(
array('apples', 'pears', 'oranges'),
array('steve', 'bob')
);

print_r($cross);


Related Topics



Leave a reply



Submit