I've had this problem bending my mind for a while now (head cold doesn't help either!), basically I have a PHP array which looks like this example:

$array[0][0] = 'apples';
$array[0][1] = 'pears';
$array[0][2] = 'oranges';

$array[1][0] = 'steve';
$array[1][1] = 'bob';

And I would like to be able to produce from this a table with every possible combination of these, but without repeating any combinations (regardless of their position), so for example this would output

Array 0            Array 1
apples             steve
apples             bob
pears              steve
pears              bob

But I would like for this to be able to work with as many different arrays as possible.

Many thanks!

share|improve this question

Will there be Array 2, Array 3, Array N? Or only two arrays? – Silver Light Mar 25 '10 at 15:05
Hi sorry for not making clearer, there could be array 2, array 3 up to array n. Thanks. – stukerr Mar 25 '10 at 15:05
What you need is a cross join done easy in SQL, but needs some thinking in PHP – Silver Light Mar 25 '10 at 15:15
As it happens this is coming from a mySQL database, all the options (apples, pears, oranges, steve, bob, etc.) are in one table kit a key to another table defining which group they are in (fruits, people etc.), any ideas how I could work that in mysql ? – stukerr Mar 25 '10 at 15:20
1  
duplicate of : stackoverflow.com/questions/2516599/… – Foo Bah Feb 9 '11 at 20:35
feedback

8 Answers

up vote 7 down vote accepted

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);
share|improve this answer
Great stuff, just what I needed. Many thanks – stukerr Mar 25 '10 at 16:13
lets put the real page there: php.net/manual/en/ref.array.php :) – Foo Bah Feb 9 '11 at 22:07
If this function lives in a class, you might want to change the user_func call like so: $c = call_user_func_array(array($this,__FUNCTION__), $_);. Also, it can give a warning (not an array) if input-arrays aren't of equal size. – Nanne Aug 21 at 14:43
feedback

You are looking for the cartesian product of the arrays, and there's an example on the php arrays site: http://php.net/manual/en/ref.array.php

share|improve this answer
feedback
foreach($parentArray as $value) {
    foreach($subArray as $value2) {
        $comboArray[] = array($value, $value2); 
    }
}

Don't judge me..

share|improve this answer
"Of course it must work when sizeof($array) >= 2." – Rob Kennedy Feb 9 '11 at 20:46
feedback

I modified this to be

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

so it returns that all-important empty array (the same result as no combinations) when you pass 0 arguments.

Only noticed this because I'm using it like

$combos = call_user_func_array('array_cartesian', $array_of_arrays);
share|improve this answer
feedback

This works I think - although after writing it I realised it's pretty similar to what others have put, but it does give you an array in the format requested. Sorry for the poor variable naming.

$output = array();
combinations($array, $output);
print_r($output);

function combinations ($array, & $output, $index = 0, $p = array()) {
    foreach ( $array[$index] as $i => $name ) {
        $copy = $p;
        $copy[] = $name;
        $subIndex = $index + 1;
        if (isset( $array[$subIndex])) {
            combinations ($array, $output, $subIndex, $copy);
        } else {
            foreach ($copy as $index => $name) {
                if ( !isset($output[$index])) {
                    $output[$index] = array();   
                }
                $output[$index][] = $name;   
            }
        }
    }
}
share|improve this answer
feedback

Syom copied http://www.php.net/manual/en/ref.array.php#54979 but I adapted it this to become an associative version:

function array_cartesian($arrays) {
  $result = array();
  $keys = array_keys($arrays);
  $reverse_keys = array_reverse($keys);
  $size = intval(count($arrays) > 0);
  foreach ($arrays as $array) {
    $size *= count($array);
  }
  for ($i = 0; $i < $size; $i ++) {
    $result[$i] = array();
    foreach ($keys as $j) {
      $result[$i][$j] = current($arrays[$j]);
    }
    foreach ($reverse_keys as $j) {
      if (next($arrays[$j])) {
        break;
      }
      elseif (isset ($arrays[$j])) {
        reset($arrays[$j]);
      }
    }
  }
  return $result;
}
share|improve this answer
feedback

In MySQL database in would be like this:

SELECT *
FROM `options`, `groups`

That's all :)

share|improve this answer
feedback
function array_comb($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;
}
share|improve this answer
feedback

Your Answer

 
or
required, but never shown
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.