Programming Challenge...Get array of variations

Greg K's picture

He has: 2,145 posts

Joined: Nov 2003

Ok, here is a fun one I had to do today. I got it, but kinda messy, tomorrow I'm working on streamlining it.

Say you have these "options" for shirts (the number are the database ID's)

Color[2]: red[11], blue[12], green[13]

Size[3]: small[21], med[22], large[23]

Sleeves[4]: Long[31], Small[32]

The data comes in in the following:

$aryData = array(2=>array(11,12,13),
                 3=>array(21,22,23),
                 4=>array(31,32));

Now I need an array of all possible variations with the values from the above array to be in this format:

$aryVariations = array(array(11,21,31), // red, small, long
                       array(11,21,32), // red, small, short
                       array(11,22,31), // red, med, long
                       array(11,22,32), // red, med, short
                       array(11,23,31), // red, large, long
                       array(11,23,32), // red, large, short
                       array(12,21,31), // blue, small, long
                      ......

[edit] forgot to mention, there is $aryVarIndex that lists the order of the attribute types $aryVarIndex = array(2,3,4); so that the names can be matched up when listing out the variations later.[/edit]

Like I said, I do have it, but the way I got it required me to do a few intermediate arrays. The project I need it on will always only have less than 50 variations.

So if you are like me, and love a challenge, let me know what you can come up with, or at the very least, what the heck the real "name" of this procedure is so I can look it up for a more optimized way.

Thanks.

-Greg

teammatt3's picture

He has: 2,102 posts

Joined: Sep 2003

That's a Cartesian product. You might be able to solve the problem with SQL.

{11,12,13} * {21,22,23} * {31,32} = {(11,21,31), (11, 21, 32), (11, 22, 31), (11, 22, 32)} etc. I had to do this by hand for my discrete math class. It look forever.

What does your DB schema look like?

And, before Abhishek Reddy has a chance to point it out, Python 2.6+ has a method available to do it for you Wink

Abhishek Reddy's picture

He has: 3,348 posts

Joined: Jul 2001

teammatt3 wrote:
And, before Abhishek Reddy has a chance to point it out, Python 2.6+ has a method available to do it for you ;)

I would never! I much prefer:

(ns example (:use [clojure.contrib.combinatorics]))

(doseq [element (apply cartesian-product some-seqs)]
  (println element))

Wink

Here's a first pass at a more complete PHP version:

<?php
function cartesian_product_appending ($array1, $array2) {
 
$result = array();
  foreach (
$array1 as $a) {
    foreach (
$array2 as $b) {
     
$r = $a;
      if (
is_array($a))
       
array_push($r, $b);
      else
       
$r = array($a, $b);
     
array_push($result, $r);
    }
  }
  return
$result;
}

function
cartesian_product_of_arrays ($arrays) {
 
$result = array_shift($arrays);
  foreach (
$arrays as $array) {
   
$result = cartesian_product_appending($result, $array);
  }
  return
$result;
}

$aryData = array(2=>array(11,12,13),
                
3=>array(21,22,23),
                
4=>array(31,32));

print_r(cartesian_product_of_arrays($aryData));

?>

Greg K's picture

He has: 2,145 posts

Joined: Nov 2003

THANK YOU! I knew there had to be a name for it. now I can google it better! (or bing it better...)

I'm about to dive into cleaning up the mess of code I have to make it, will post the final version here (or a link to a function that will do the same for me)

For the gist of the DB:

tblAttribute
AttributeID
AttributeName

tblAttributeValue
AttributeValueID
AttriuteID - FK back to tblAttribute
AttributeValue

tblProductAttVal
ProductAttValID
AttributeValueID - FK back to tblAttributeValue
Available - Basically if set, this value is available on this product

note there are other values in use, and there are needs that require it broken down this way out of the scope of my needs here, but this is the gist of the table/fields that affect my challenge.

-Greg

Greg K's picture

He has: 2,145 posts

Joined: Nov 2003

Ok, now that I know what to look for, there is an example function right in the comments on php.net to do it. I'll have to adapt it a bit, but should work.

-Greg

Greg K's picture

He has: 2,145 posts

Joined: Nov 2003

Here is what I finally came up with that, trust me, is cleaner that than last nights code big time.

// $aryMatrix = contains the data, and at this point already know there
//              there is at least one combination and less than 50

$intMaxVar = 1; // Will hold the maximum # of variations
foreach($aryMatrix as $aryMatrixData) $intMaxVar *= count($aryMatrixData);

$aryVariations = array_fill(0,$intMaxVar,array(array_fill(0,count($aryMatrix),0));
$intSecOffset = $intMaxVar;
for($iAtt=0; $iAtt<count($aryData); $iAtt++) {
   $intAttValCount  = count($aryMatrix[$iAtt]['ValIDs']);
   $intSets = $intSecOffset / $intAttValCount;
   for ($iAttVal=0; $iAttVal<$intAttValCount; $iAttVal++) {
      for ($iOffset=0; $iOffset<$intMaxVar; $iOffset+=$intSecOffset) {
         for ($iSetPos=0; $iSetPos<$intSets; $iSetPos++) {
            $aryVariations[$iAttVal*$intSets+$iOffset+$iSetPos][$iAtt] = $aryMatrix[$iAtt]['ValIDs'][$iAttVal];
   }  }  }
   $intSecOffset /= $intAttValCount;
}

Yeah, it loops through total combinations * # attributes, but I know I have a set # for both of these.

Now, the next step I may tackle, here is how the data is originally, (which gets converted to the aryMatrix that I mentioned in my first post):

$aryData = array( ATT_ID => array( 'Name'=> ATT_NAME, 'Values'=> array(VAL_ID => VAL_NAME)),
                  ATT_ID => array( 'Name'=> ATT_NAME, 'Values'=> array(VAL_ID => VAL_NAME)),
(etc)

I think I'm good where I am at. Abhishek, I'll try out your code tomorrow, and do some resource checking/timing tests to see which route to take. I am very grateful for your post, as I just wet back through my entire history in firefox for today, and i cannot find the solution on the PHP documentation site! weird!

-Greg

Want to join the discussion? Create an account or log in if you already have one. Joining is fast, free and painless! We’ll even whisk you back here when you’ve finished.