Recursive strings replacement with PHP
For a project I'm working on, I have a base URI with placeholders and I want to generate all the possible combinations from an array of possible values for each placeholder using PHP.
More concretely:
<?php
$uri = "foo/bar?foo=%foo%&bar=%bar%";
$placeholders = array(
'%foo%' => array('a', 'b'),
'%bar%' => array('c', 'd'),
// ...
);
I'd like ending up having the following array:
array(4) {
[0]=>开发者_运维知识库;
string(23) "foo/bar?foo=a&bar=c"
[1]=>
string(23) "foo/bar?foo=a&bar=d"
[2]=>
string(19) "foo/bar?foo=b&bar=c"
[3]=>
string(19) "foo/bar?foo=b&bar=d"
}
Not to mention I should be able to add more placeholders to generate more computed uris, of course, so the solution should work recursively.
I might be overtired these days, but I'm getting stuck at achieving this simply, and I'm sure there's a simple way, perhaps even with builtin PHP functions…
Hints? Any help much appreciated.
<?php
function rec($values,$keys,$index,$str,&$result)
{
if($index<count($values))
foreach($values[$index] as $val)
rec($values,$keys,$index+1,$str.substr($keys[$index],1,strlen($keys[$index])-2)."=".$val."&",$result);
else
$result[count($result)] = $str;
}
// Now for test
$placeholders = array(
'%foo%' => array('a', 'b'),
'%bar%' => array('c', 'd' , 'h'),
);
$xvalues = array_values($placeholders) ;
$xkeys = array_keys($placeholders) ;
$result = array();
rec($xvalues,$xkeys,0,"",$result); // calling the recursive function
print_r($result);
// the result will be:
Array (
[0] => foo=a&bar=c&
[1] => foo=a&bar=d&
[2] => foo=a&bar=h&
[3] => foo=b&bar=c&
[4] => foo=b&bar=d&
[5] => foo=b&bar=h&
)
?>
It handles unlimited count of placeholders & unlimited count of values
$uri= "foo/bar?foo=%foo%&bar=%bar%&baz=%baz%";
$placeholders = array(
'%foo%' => array('a', 'b'),
'%bar%' => array('c', 'd', 'e'),
'%baz%' => array('f', 'g')
);
//adds a level of depth in the combinations for each new array of values
function expandCombinations($combinations, $values)
{
$results = array();
$i=0;
//combine each existing combination with all the new values
foreach($combinations as $combination) {
foreach($values as $value) {
$results[$i] = is_array($combination) ? $combination : array($combination);
$results[$i][] = $value;
$i++;
}
}
return $results;
}
//generate the combinations
$patterns = array();
foreach($placeholders as $pattern => $values)
{
$patterns[] = $pattern;
$combinations = isset($combinations) ? expandCombinations($combinations, $values) : $values;
}
//generate the uris for each combination
foreach($combinations as $combination)
{
echo str_replace($patterns, $combination, $uri),"\n";
}
The idea here is to list in an array all the possible combinations for the replacements. The function expandCombinations just adds one level of depth in the combinations for each new pattern to replace with no recursion (we know how PHP loves recursion). This should allow for a decent number of patterns to replace without recursing at an insane depth.
A recursive solution:
function enumerate($uri, $placeholders){
$insts = array();
if (!empty($placeholders)){
$key = array_keys($placeholders)[0];
$values = array_pop($placeholders);
foreach($values => $value){
$inst = str_replace($uri, $key, $value);
$insts = array_merge($insts, (array)enumerate($inst, $placeholders));
}
return $insts;
} else {
return $uri;
}
}
Each call to the function pops one placeholder off the array and loops through its potential values enumerating through all the remaining placeholder values for each one. The complexity is O(k^n) where k is the average number of replacements for each placeholder and n is the number of placeholders.
My PHP is a little rusty; let me know if I got any of the syntax wrong.
foreach($placeholders['%foo%'] as $foo){
foreach($placeholders['%bar%'] as $bar){
$container[] = str_replace(array('%foo%','%bar%'),array($foo,$bar),$uri);
}
}
This work, but it's not so elegant because of the need to get rid of the placeholder array keys :
<?php
/*
* borrowed from http://www.theserverpages.com/php/manual/en/ref.array.php
* author: skopek at mediatac dot com
*/
function array_cartesian_product($arrays) {
//returned array...
$cartesic = array();
//calculate expected size of cartesian array...
$size = sizeof($arrays) > 0 ? 1 : 0;
foreach ($arrays as $array) {
$size *= sizeof($array);
}
for ($i = 0; $i < $size; $i++) {
$cartesic[$i] = array();
for ($j = 0; $j < sizeof($arrays); $j++) {
$current = current($arrays[$j]);
array_push($cartesic[$i], $current);
}
//set cursor on next element in the arrays, beginning with the last array
for ($j = sizeof($arrays) - 1; $j >= 0; $j--) {
//if next returns true, then break
if (next($arrays[$j])) {
break;
} else { //if next returns false, then reset and go on with previuos array...
reset($arrays[$j]);
}
}
}
return $cartesic;
}
$uri = "foo/bar?foo=%foo%&bar=%bar%";
$placeholders = array(
0 => array('a', 'b'), // '%foo%'
1 => array('c', 'd'), // '%bar%'
);
//example
header("Content-type: text/plain");
$prod = array_cartesian_product($placeholders);
$result = array();
foreach ($prod as $vals) {
$temp = str_replace('%foo%', $vals[0], $uri);
$temp = str_replace('%bar%', $vals[1], $temp);
array_push($result, $temp);
}
print_r($result);
?>
精彩评论