0

I have an array which has different items example shirts, shoes, ties, jackets etc. And each of these items can have multiple designs denoted by their ids.

$variations = array(
    'shirts' => array('2'),
    'shoes' => array('7', '3'),
    'jackets' => array('1', '5')
);

Now we are looking for an efficient way to create different variations of all of these.

## Required result ##
$result = array(
   array('2','7','1'),
   array('2', '7', '5'),
   array('2','3','1'),
   array('2','3','5')
);

Any help would be appreciated :)

EDIT: Our current function

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;
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Omer Farooq
  • 3,754
  • 6
  • 31
  • 60
  • 2
    Are you facing any particular coding problem, or just asking somebody to do the job? – Alex Blex May 18 '17 at 08:05
  • we are looking for better performing code. here is our current implementation. – Omer Farooq May 18 '17 at 08:22
  • It would be fair to mention credits for "your" function borrowed from http://stackoverflow.com/questions/6311779/finding-cartesian-product-with-php-associative-arrays – Alex Blex May 18 '17 at 08:31
  • We currently have this function in our system we are looking to improve the functionality. i never said we built this funciton are looking to improve it. – Omer Farooq May 18 '17 at 08:37
  • Still fail to see how the question "shows any research effort". There are several other implementations on SO, eg http://stackoverflow.com/questions/2516599/php-2d-array-output-all-combinations if you are not happy with any of them, please voice your concerns. – Alex Blex May 18 '17 at 08:42

2 Answers2

0

While I agree with comments beneath your question I implemented generator-based solution for fun so I can share it anyway:

$variations = array(
    'shirts' => array('2'),
    'shoes' => array('7', '3'),
    'jackets' => array('1', '5')
);

var_dump(iterator_to_array(cartesian($variations), false));

function cartesian($lists, $product = []) 
{
        if (empty($product)) {
                // first run, reverse array for array_pop and remove empty lists
                $lists = array_reverse(array_filter($lists, 'count'));
        }
        $curr = array_pop($lists);
        foreach ($curr as $c) {
                if (empty($lists)) {
                        yield array_merge($product, [$c]);
                } else {
                        yield from cartesian($lists, array_merge($product, [$c]));
                }
        }
}
malarzm
  • 2,831
  • 2
  • 15
  • 25
0

This is how I did it

$parameters = array(
    'shirts' => array('2'),
    'shoes' => array('7', '3'),
    'jackets' => array('1', '5')
);    
$arPhrases = $parameters[0];

for ($i = 1; $i < count($parameters); $i++) {
    $notFullCount = count($arPhrases);
    foreach ($arPhrases as $phrase) {
        foreach ($parameters[$i] as $newPart) {
            $arPhrases[] = $phrase." ".$newPart;
        }
    }
    $arPhrases = array_slice($arPhrases, $notFullCount);
}