PHP Array: join each sub-array together (Probability)

loops, multidimensional-array, php, recursion

Solution

I tried another approach but eventually ended with something similar to Valentin CLEMENT's solution although my function is more verbose.

Still, the originality is that this function gets you a tree of combinations, which may (or may not) be usefull depending on what you intend to do.

Here is the code :

function getCombinations( $arrayList, $index = 0  )
{
    $subCombinations = $combinations = '';

    if ( $index < count( $arrayList )-1 )
    {
        $subCombinations = getCombinations( $arrayList,  $index+1 );
    }

    foreach( $arrayList[$index] as $item )
    {
        $combinations[$item] = $subCombinations ;
    }
    return $combinations;
}

$combinations = getCombinations( $array );

print_r( $combinations );

With example data :

$array = array(
    array('a', 'b', 'c'),
    array('e', 'f', 'g'),
    array('h', 'i', 'j', 'k', 'l')
);

It will output :

Array
(
    [a] => Array
        (
            [e] => Array
                (
                    [h] => 
                    [i] => 
                    [j] => 
                    [k] => 
                    [l] => 
                )

            [f] => Array
                (
                    [h] => 
                    [i] => 
                    [j] => 
                    [k] => 
                    [l] => 
                )

            [g] => Array
                (
                    [h] => 
                    [i] => 
                    [j] => 
                    [k] => 
                    [l] => 
                )

        )

    [b] => Array
        (
            [e] => Array
                (
                    [h] => 
                    [i] => 
                    [j] => 
                    [k] => 
                    [l] => 
                )

            [f] => Array
                (
                    [h] => 
                    [i] => 
                    [j] => 
                    [k] => 
                    [l] => 
                )

            [g] => Array
                (
                    [h] => 
                    [i] => 
                    [j] => 
                    [k] => 
                    [l] => 
                )

        )

    [c] => Array
        (
            [e] => Array
                (
                    [h] => 
                    [i] => 
                    [j] => 
                    [k] => 
                    [l] => 
                )

            [f] => Array
                (
                    [h] => 
                    [i] => 
                    [j] => 
                    [k] => 
                    [l] => 
                )

            [g] => Array
                (
                    [h] => 
                    [i] => 
                    [j] => 
                    [k] => 
                    [l] => 
                )

        )

)

And then it requires additional code to draw expected result :

function drawCombinations( $combinations, $line = array() )
{
    foreach( $combinations as $value => $children )
    {
        array_push( $line, $value );

        if ( is_array( $children ) ) 
        {
            drawCombinations( $children, $line );
        }
        else
        { 
            echo implode( " ", $line ) ." \n";
        }

        array_pop( $line );
    }

}


drawCombinations( $combinations );

To produce :

a e h
a e i
a e j
a e k
a e l
a f h
a f i
a f j
a f k
a f l
a g h
a g i
a g j
a g k
a g l
b e h
b e i
b e j
b e k
b e l
b f h
b f i
b f j
b f k
b f l
b g h
b g i
b g j
b g k
b g l
c e h
c e i
c e j
c e k
c e l
c f h
c f i
c f j
c f k
c f l
c g h
c g i
c g j
c g k
c g l

As I said eariler, if you don't need this tree of results (that your questions did not mention, I just produced this while searching the best way), Valentin CLEMENT's approach could be better (if you don't use too large dataset, I will explain why after).

I rewrote it a bit, in a way I think more readable and usable :

function expand( $array, $from = 0, $length = false ) 
{
    if ( $length === false )
    {
        $length = count( $array );
    }

    if ( $length == $from )
    {
        return array('');
    }
    else 
    {
        $result = array();

        foreach( $array[$from] as $x ) 
        {
            foreach( expand( $array, $from+1, $length ) as $tail )
            {
                $result[] = trim("$x $tail");
            }
        }
        return $result;
    }
}


$combinations = expand( $array );

print_r( $combinations );

It returns the following array :

Array
(
    [0] => a e h
    [1] => a e i
    [2] => a e j
    [3] => a e k
    [4] => a e l
    [5] => a f h
    [6] => a f i
    [7] => a f j
    [8] => a f k
    [9] => a f l
    [10] => a g h
    [11] => a g i
    [12] => a g j
    [13] => a g k
    [14] => a g l
    [15] => b e h
    [16] => b e i
    [17] => b e j
    [18] => b e k
    [19] => b e l
    [20] => b f h
    [21] => b f i
    [22] => b f j
    [23] => b f k
    [24] => b f l
    [25] => b g h
    [26] => b g i
    [27] => b g j
    [28] => b g k
    [29] => b g l
    [30] => c e h
    [31] => c e i
    [32] => c e j
    [33] => c e k
    [34] => c e l
    [35] => c f h
    [36] => c f i
    [37] => c f j
    [38] => c f k
    [39] => c f l
    [40] => c g h
    [41] => c g i
    [42] => c g j
    [43] => c g k
    [44] => c g l
)

And it is then easy to achieve expected result :

echo implode( "\n", $combinations )."\n";

Will output :

a e h
a e i
a e j
a e k
a e l
a f h
a f i
a f j
a f k
a f l
a g h
a g i
a g j
a g k
a g l
b e h
b e i
b e j
b e k
b e l
b f h
b f i
b f j
b f k
b f l
b g h
b g i
b g j
b g k
b g l
c e h
c e i
c e j
c e k
c e l
c f h
c f i
c f j
c f k
c f l
c g h
c g i
c g j
c g k
c g l

I thought, at first, that my solution was cosuming more memory than Valentin's one because it uses arrays, but when I tested it out, I realized that it indeed it uses slightly less memory.

Displaying the memory metrics against the two methods gave those results :

drawCombinations(  getCombinations( $array ));

echo memory_get_usage()." ". memory_get_peak_usage()."\n";

// 238736 244896





echo implode( "\n", expand( $array ) )."\n";

echo memory_get_usage()." ". memory_get_peak_usage()."\n";

// 238912 252304

But it gets more important when using bigger input values, with :

$array = array(
    array('a', 'b', 'c'),
    array('e', 'f', 'g'),
    array('h', 'i', 'j', 'k', 'l'),
    array('m', 'n', 'o', 'p', 'q', 'r', 's'),
    array('t', 'u', 'v', 'x', 'y', 'z')
);

getCombinations gives :

drawCombinations(  getCombinations( $array ));

echo memory_get_usage()." ". memory_get_peak_usage()."\n";

// 242376 253008

expand gives :

echo implode( "\n", expand( $array ) )."\n";

echo memory_get_usage()." ". memory_get_peak_usage()."\n";

//242544 704520

The reason is obvious if we look at the array produced by each function, since the first solution stores less duplicate values (I am not sure how PHP handles the duplicates arrays ending each branch of the tree).

Once again, depending on what you plain to achieve, you will care or not.

"Echoing" each rows on the fly instead of creating a big result array decreases slightly the memory peak issue but expand() remains more memory consuming as dataset grows.

I hope it helps, at least that was interesting to me ;)

Problem

I want simply to find-out better way to do this: ``` $array = array( array('a', 'b', 'c'), array('e', 'f', 'g'), array('h', 'i', 'j', 'k', 'l') ); ``` The goals is to print something like this: ``` a e h a e i a e j a e k a e l a f h a f i a f j a f k a f l a g h a g i a g j a g k a g l ``` Then doing same for `b` and `c`. Currently, I am using this code: ``` foreach ($array[0] as $val1) { foreach ($array[1] as $val2) { foreach ($array[2] as $val3) { echo "$val1 $val2 $val3 \n"; } echo "--------\n"; } } ``` I also tried to create above code dynamically and execute it with eval: ``` $eval = ' $data =array(); '; $eval_blocks = ''; $eval_foreach = ''; $eval_data = ' $data[] = '; $looplength = count($array); for ($i = 0; $i < $looplength; $i++) { $eval_foreach .= ' foreach($array[' . $i . '] as $val' . ($i + 1) . '){ '; if (($i + 1) == $looplength) { $eval_data .= ' $val' . ($i + 1) . ';'; } else { $eval_data .= ' $val' . ($i + 1) . ' ." ".'; } $eval_blocks .= ' } '; } $eval = $eval . $eval_foreach . $eval_data . $eval_blocks; eval($eval); print_r($data); ``` But I still want to find better way for doing this, if possible. Update: Note: the `$array` is dynamic, It might contain two sub-array or more

Original source

Related problems