开发者

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

?>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜