I have an unknown number of arrays, each containing an unknown number of words. I want to concatenate the values from each list so that all possible variations of the words are stored to a final array.
我有一个未知数量的数组,每个数组包含未知数量的单词。我希望连接每个列表中的值,以便将单词的所有可能变体存储到最终数组中。
For example, if array 1 contains:
例如,如果数组1包含:
dog
cat
and array 2 contains:
和数组2包含:
food
tooth
and array 3 contains:
和数组3包含:
car
bike
I'd like the output to be:
我希望输出为:
dog food car
dog food bike
dog tooth car
dog tooth bike
cat food car
cat food bike
cat tooth car
cat tooth bike
There could be more than 3 lists, and each list will most likely have more than 2 words.
可能有超过3个列表,每个列表最有可能超过2个单词。
I'd like to do this in PHP.
我想在PHP中这样做。
I know how to do it if I know the number of lists, though it's probably not the most resource efficient method. But nested foreach
loops works if you know the number of arrays. What if you don't? And what are some methods to solve this problem that will still work if, let's say, there are 100 arrays of 100 words each. Or 1000?
如果我知道列表的数量,我知道如何做到这一点,尽管它可能不是资源效率最高的方法。但是,如果知道数组的数量,嵌套的foreach循环就可以工作。如果你不这样做怎么办?还有什么方法可以解决这个问题,如果让我们说有100个数组,每个100个单词,那么它仍然有用。还是1000?
Thanks!
谢谢!
4 个解决方案
#1
9
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 car
dog food bike
dog tooth car
dog tooth bike
cat food car
cat food bike
cat tooth car
cat 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:
通过这种方法,也很容易得到“子连续”。只需插入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
#2
5
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 ...(元素数)-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]
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.)
您所需要的只是一个(整数)索引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";
}
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)
您可以使用它来实现,例如Iterator,SeekableIterator或甚至是ArrayAccess(从而与递归解决方案相比反转控件,几乎就像python或ruby中的yield)
<?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, 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
( the sequence seems to be ok ;-) )
(序列似乎没问题;-))
#3
2
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
#4
2
My take
我的看法
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();
#1
9
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 car
dog food bike
dog tooth car
dog tooth bike
cat food car
cat food bike
cat tooth car
cat 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:
通过这种方法,也很容易得到“子连续”。只需插入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
#2
5
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 ...(元素数)-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]
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.)
您所需要的只是一个(整数)索引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";
}
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)
您可以使用它来实现,例如Iterator,SeekableIterator或甚至是ArrayAccess(从而与递归解决方案相比反转控件,几乎就像python或ruby中的yield)
<?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, 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
( the sequence seems to be ok ;-) )
(序列似乎没问题;-))
#3
2
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
#4
2
My take
我的看法
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();