Concatenate values of n arrays in php Concatenate values of n arrays in php arrays arrays

Concatenate values of n arrays in php


You can put all word arrays into one array and use a recursive function like this:

function concat(array $array) {    $current = array_shift($array);    if(count($array) > 0) {        $results = array();        $temp = concat($array);        foreach($current as $word) {          foreach($temp as $value) {            $results[] =  $word . ' ' . $value;          }        }        return $results;               }    else {       return $current;    }}$a = array(array('dog', 'cat'), array('food', 'tooth'), array('car', 'bike'));print_r(concat($a));

Which returns:

Array(    [0] => dog food car    [1] => dog food bike    [2] => dog tooth car    [3] => dog tooth bike    [4] => cat food car    [5] => cat food bike    [6] => cat tooth car    [7] => cat tooth bike)

But I guess this behaves badly for large arrays as the output array will be very big.


To get around this, you can output the combinations directly, using a similar approach:

function concat(array $array, $concat = '') {    $current = array_shift($array);    $current_strings = array();    foreach($current as $word) {            $current_strings[] = $concat . ' ' . $word;    }    if(count($array) > 0) {        foreach($current_strings as $string) {            concat($array, $string);        }           }    else {      foreach($current_strings as $string) {          echo $string . PHP_EOL;      }       }}concat(array(array('dog', 'cat'), array('food', 'tooth'), array('car', 'bike')));

Which gives:

dog food cardog food bikedog tooth cardog tooth bikecat food carcat food bikecat tooth carcat tooth bike

With this approach it is also easy to get the "sub-concatinations". Just insert echo $string . PHP_EOL; before concat($array, $string); and the output is:

 dog dog food dog food car dog food bike dog tooth dog tooth car dog tooth bike cat cat food cat food car cat food bike cat tooth cat tooth car cat tooth bike


You can enumerate the elements of the result set, i.e. for each integer between 0....(number of elements)-1 you can tell which element to return (i.e. there is a natural order). For the given example:

0 => array1[0], array2[0], array3[0]1 => array1[0], array2[0], array3[1]2 => array1[0], array2[1], array3[0]7 => array1[1], array2[1], array3[1]

All you need is a (integer) index n and a function that "translates" the index to the nth element of the (natural ordered) set. Since you only need an integer to store the current state the memory consumption doesn't "explode" when you have many/large arrays. As chris said in his comment, you trade speed (when using smaller sets) for low memory consumption. (Though I think -the way php is implemented- this is also a reasonable fast solution.)

$array1 = array('dog', 'cat');$array2 = array('food', 'tooth');$array3 = array('car', 'bike');function foo( $key /* , ... */ ) {  $params = func_get_args();  $rv = array();  $key = array_shift($params);  $i=count($params);  while( 0 < $i-- ) {    array_unshift($rv, $params[$i][ $key % count($params[$i]) ]);    $key = (int)($key / count($params[$i]));  }  return $rv;}for($i=0; $i<8; $i++) {  $a = foo($i, $array1, $array2, $array3);  echo join(', ', $a), "\n";}

You can use this to implement e.g. an Iterator, a SeekableIterator or maybe even an ArrayAccess (and thereby inverting the control compared to the recursive solutions, almost like a yield in python or ruby)

<?php$array1 = array('dog', 'cat', 'mouse', 'bird');$array2 = array('food', 'tooth', 'brush', 'paste');$array3 = array('car', 'bike', 'plane', 'shuttlecraft');$f = new Foo($array1, $array2, $array3);foreach($f as $e) {  echo join(', ', $e), "\n";}class Foo implements Iterator {  protected $data = null;  protected $limit = null;  protected $current = null;  public function __construct(/* ... */ ) {      $params = func_get_args();    // add parameter arrays in reverse order so we can use foreach() in current()    // could use array_reverse(), but you might want to check is_array() for each element.    $this->data = array();    foreach($params as $p) {      // <-- add: test is_array() for each $p  -->      array_unshift($this->data, $p);    }    $this->current = 0;    // there are |arr1|*|arr2|...*|arrN| elements in the result set    $this->limit = array_product(array_map('count', $params));  }  public  function current() {    /* this works like a baseX->baseY converter (e.g. dechex() )       the only difference is that each "position" has its own number of elements/"digits"    */    // <-- add: test this->valid() -->    $rv = array();    $key = $this->current;    foreach( $this->data as $e) {      array_unshift( $rv, $e[$key % count($e)] );      $key = (int)($key/count($e));    }    return $rv;  }  public function key() { return $this->current;  }  public function next() { ++$this->current; }  public function rewind () { $this->current = 0; }  public function valid () { return $this->current < $this->limit; }}

prints

dog, food, cardog, food, bikedog, food, planedog, food, shuttlecraftdog, tooth, cardog, tooth, bike[...]bird, paste, bikebird, paste, planebird, paste, shuttlecraft

( the sequence seems to be ok ;-) )


I haven't tested this on huge word lists, but it's pretty fast on moderately sized lists and doesn't use recursion, which I think (please correct me if I'm wrong) is probably causing the memory limit problems:

$lines = array('');foreach ($arrays as $array) {  $old_lines = $lines;  $lines = array();  foreach ($array as $word) {    foreach ($old_lines as $line) {      $lines[] = trim($line .' '. $word);    } // foreach  } // foreach} // foreach