phparrayscombinationscartesian-product

php multi array


What if I have a multidimensional array, in which I want to make combinations. So for example if I have 3 arrays:

Then I want a function which make combinations like: array( array(1,7) array(1,3) array( 3,9) etcetera but also array(7,3) array(7,9)

Plus it must check the availability so if the numbers are not available then do not make combinations with them.

Now I got this, and further I cant get:

   $xCombinations =
            Array(
                Array(1,2)
                Array(7,3)
                Array(3,9,8,2)
            );



            $available = Array([1] => 2,[2] => 1, [7]=>3,[3]=>4, [8]=>2, [2]=>4);

            $combination = Array();

            recursiveArray($xCombinations);

            function recursiveArray($tmpArr){
               if($tmpArr){
                 foreach ($tmpArr)as $value) {
                   if (is_array($value)) {
                      displayArrayRecursively($value);
                   } else {
                    //here 1st time $value would be 1, 
                    //so this must now be combined with 7 , 3 , 9, 8 ,2 > 
                    //so you will get an array of array(1,7) array, (1,3) array (3,9) 
                    //etcetera.. there arrays must be also in a multidimensional array
                   }
                 }
               }
            }

Solution

  • This issue consists ow two parts. Actually, it can be implemented with one functional block, but that would be hard to read. So, I'll split it to two operations.

    Retrieving combinations

    First task is to get all combinations. By definition, those combinations are Cartesian product of your array members. That could be produced without recursion, like:

    $result = array_reduce(array_slice($input, 1), function($c, $x)
    {
       return call_user_func_array('array_merge', 
          array_map(function($y) use ($c)
          {
             return array_map(function($z) use ($y)
             {
                return array_merge((array)$z, (array)$y);
             }, $c);
          }, $x)
       );
    }, current($input));
    

    Here we're accumulating our tuples one be one. Result is just all possible combinations as it should be for Cartesian product.

    Filtering result

    This is another part of the issue. Assuming you have $available as a set of key=>value pairs, where key corresponds to specific number in a tuple and value corresponds to how many times can that number appear in that tuple at most, here's simple filtering code:

    $result = array_filter($result, function($tuple) use ($available)
    {
       foreach(array_count_values($tuple) as $value=>$count)
       {
          if(isset($available[$value]) && $available[$value]<$count)
          {
             return false;
          }
       }
       return true;
    });
    

    For instance, if we have $input as

    $input = [
       [1,3],
       [1,6]
    ];
    

    and restrictions on it as

    $available = [6=>0, 1=>1];
    

    Then result would be only

    array(1) {
      [1]=>
      array(2) {
        [0]=>
        int(3)
        [1]=>
        int(1)
      }
    }
    

    since only this tuple fulfills all requirements (so it has not 6 and 1 appeared once in it)