Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Here's a solution I wouldn't be ashamed to show.</p> <h2>Rationale</h2> <p>Assume that we have an input array <code>$input</code> with <code>N</code> sub-arrays, as in your example. Each sub-array has <code>Cn</code> items, where <code>n</code> is its index inside <code>$input</code>, and its key is <code>Kn</code>. I will refer to the <code>i</code>th item of the <code>n</code>th sub-array as <code>Vn,i</code>.</p> <p>The algorithm below can be proved to work (barring bugs) by induction:</p> <p>1) For N = 1, the cartesian product is simply <code>array(0 =&gt; array(K1 =&gt; V1,1), 1 =&gt; array(K1 =&gt; V1,2), ... )</code> -- C1 items in total. This can be done with a simple <code>foreach</code>.</p> <p>2) Assume that <code>$result</code> already holds the cartesian product of the first N-1 sub-arrays. The cartesian product of <code>$result</code> and the Nth sub-array can be produced this way:</p> <p>3) In each item (array) inside <code>$product</code>, add the value <code>KN =&gt; VN,1</code>. Remember the resulting item (with the added value); I 'll refer to it as <code>$item</code>.</p> <p>4a) For each array inside <code>$product</code>:</p> <p>4b) For each value in the set <code>VN,2 ... VN,CN</code>, add to <code>$product</code> a copy of <code>$item</code>, but change the value with the key <code>KN</code> to <code>VN,m</code> (for all <code>2 &lt;= m &lt;= CN</code>).</p> <p>The two iterations 4a (over <code>$product</code>) and 4b (over the Nth input sub-array) ends up with <code>$result</code> having <code>CN</code> items for every item it had before the iterations, so in the end <code>$result</code> indeed contains the cartesian product of the first N sub arrays.</p> <p>Therefore the algorithm will work for any N.</p> <p><em>This was harder to write than it should have been. My formal proofs are definitely getting rusty...</em></p> <h2>Code</h2> <pre><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 =&gt; $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 &amp;$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; } </code></pre> <h2>Usage</h2> <pre><code>$input = array( 'arm' =&gt; array('A', 'B', 'C'), 'gender' =&gt; array('Female', 'Male'), 'location' =&gt; array('Vancouver', 'Calgary'), ); print_r(cartesian($input)); </code></pre> <h2><a href="http://ideone.com/7mRCL" rel="noreferrer">See it in action!</a></h2>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload