在php中连接n个数组的值

时间:2010-02-11 18:00:54

标签: php arrays concatenation combinatorics

我有一个未知数量的数组,每个数组包含未知数量的单词。我希望连接每个列表中的值,以便将所有可能的单词变体存储到最终数组中。

例如,如果数组1包含:

dog
cat

和数组2包含:

food
tooth

和数组3包含:

car
bike

我希望输出为:

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

可能有超过3个列表,每个列表最有可能超过2个单词。

我想在PHP中这样做。

如果我知道列表的数量,我知道该怎么做,尽管它可能不是资源效率最高的方法。但是如果知道数组的数量,嵌套的foreach循环就可以工作。如果你不这样做怎么办?有什么方法可以解决这个问题,如果让我们说有100个100个单词的数组,那么这个方法仍然有效。还是1000?

谢谢!

4 个答案:

答案 0 :(得分:9)

您可以将所有单词数组放入一个数组中,并使用递归函数,如下所示:

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));

返回:

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
)

但是我觉得这对大型数组来说很糟糕,因为输出数组会非常大。


要解决这个问题,您可以使用类似的方法直接输出组合:

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')));

给出了:

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

通过这种方法,也可以很容易地得到“次级联合”。只需在echo $string . PHP_EOL;之前插入concat($array, $string);,输出为:

 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

答案 1 :(得分:5)

您可以枚举结果集的元素,即对于0 ...(元素数)-1之间的每个整数,您可以判断返回哪个元素(即,存在自然顺序)。对于给定的示例:

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]

您需要的只是一个(整数)索引 n 和一个将索引“转换”为(自然有序)集合的 n 元素的函数。由于您只需要一个整数来存储当前状态,因此当您有许多/大型数组时,内存消耗不会“爆炸”。正如克里斯在评论中所说的那样,你交换速度(当使用较小的套装时)用于低内存消耗。 (虽然我认为 - 实现php的方式 - 这也是一个合理的快速解决方案。)

$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";
}

您可以使用它来实现,例如一个Iterator,一个 SeekableIterator或甚至一个ArrayAccess(因此与递归解决方案相比,反转控件,几乎就像yield一样python或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; }
}

打印

dog, food, car
dog, food, bike
dog, food, plane
dog, food, shuttlecraft
dog, tooth, car
dog, tooth, bike
[...]
bird, paste, bike
bird, paste, plane
bird, paste, shuttlecraft

(序列似乎没问题;-))

答案 2 :(得分:2)

我没有在大字列表上测试过这个,但是在中等大小的列表上它很快并且不使用递归,我认为(如果我错了请纠正我)可能导致内存限制问题:

$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

答案 3 :(得分:2)

我的看法

class Combinator
{
     protected $words;
     protected $combinator;

     public function __construct($words, $combinator = null)
     {
         $this->words = $words;
         $this->combinator = $combinator;
     }

     public function run($combo = '')
     {
         foreach($this->words as $word) {
             if($this->combinator !== null) {
                 $this->combinator->run("$combo $word"); 
             } else {
                 echo "$combo $word", PHP_EOL;
             }
         }
     }
}

$c = new Combinator(array('dog', 'cat'), 
                    new Combinator(array('food', 'tooth'),
                                   new Combinator(array('car', 'bike'))));

$c->run();