4

I have array symbols what I want replace, but I need generate all possibillity

$lt = array(
    'a' => 'ą',
    'e' => 'ę',
    'i' => 'į',
);

For example if I have this string:

tazeki

There can be huge amount of results:

tązeki
tazęki
tązęki
tazekį
tązekį
tazękį
tązękį

My question is what formula use to have all variants ?

Rizier123
  • 58,877
  • 16
  • 101
  • 156
Wizard
  • 10,985
  • 38
  • 91
  • 165

7 Answers7

6

This should work for you, easy and simple:

What does this code do?

1. Data part

In the data part I just define the string and the replacement's for the single character with a associative array (search character as key, replacement as value).

2. getReplacements() function

This function get's all combinations of the characters which have to be replaced in this format:

key   = index in the string
value = character

So in this code example the array would look something like this:

Array (
    [0] => Array (
        [1] => a
    )    
    [1] => Array (
        [3] => e
    )   
    [2] => Array (
        [3] => e
        [1] => a
    )    
    [3] => Array (
        [5] => i
    )    
    [4] => Array (
        [5] => i
        [1] => a
    )    
    [5] => Array (
        [5] => i
        [3] => e
    )    
    [6] => Array (
        [5] => i
        [3] => e
        [1] => a
    )

)

As you can see this array holds all combinations of the characters which have to be replaced, in this format:

[0] => Array (
     //^^^^^ The entire sub array is the combination which holds the single characters which will be replaced
    [1] => a
   //^     ^ A single character of the full combination which will be replaced
   //| The index of the character in the string (This is that it also works if you have a character multiple times in your string)
   // e.g. 1 ->  t *a* z e k i
   //            ^  ^  ^ ^ ^ ^
   //            |  |  | | | |
   //            0 *1* 2 3 4 5
)    

So how does it gets all combinations?

Pretty simple I loop through every single character which I want to replace with a foreach loop and then I go through every single combination which I already have and combine it with the character which is currently the value of the foreach loop.

But to get this to work you have to start with a empty array. So as a simple example to see and understand what I mean:

Characters which have to be replaced (Empty array is '[]'): [1, 2, 3]

                               //new combinations for the next iteration
                               |
Character loop for NAN*:

    Combinations:
                  - []         |  -> []

Character loop for 1:

    Combinations:
                  - []    + 1  |  -> [1]    

Character loop for 2:

    Combinations:
                  - []    + 2  |  -> [2]
                  - [1]   + 2  |  -> [1,2]    

Character loop for 3:

    Combinations:
                  - []    + 3  |  -> [3]
                  - [1]   + 3  |  -> [1,3]
                  - [2]   + 3  |  -> [2,3]         
                  - [1,2] + 3  |  -> [1,2,3]    
                               //^ All combinations here

* NAN: not a number

So as you can see there is always: (2^n)-1 combinations in total. Also from this method there is a empty array left in the combination array, so before I return the array I just use array_filter() to remove all empty arrays and array_values() to reindex the entire array.

3. Replacement part

So to get all characters from the string where will build the combinations out of I use this line:

array_intersect(str_split($str), array_keys($replace))

This just get's all coincidences with array_intersect() from the string as array with str_split() and the keys from the replace array with array_keys().

In this code the array which you pass to the getReplacements() function would look something like this:

Array
(
    [1] => a
   //^     ^ The single character which is in the string and also in the replace array
   //| Index in the string from the character
    [3] => e
    [5] => i
)

4. Replace all combinations

At the end you only have to replace all combinations in the source string with the replace array. For this I loop just through every combination and replace every single character in the string from the combination with the matching character from the replace array.

This can be simply done with this line:

$tmp = substr_replace($tmp, $replace[$v], $k, 1);
     //^^^^^^^^^^^^^^       ^^^^^^^^^^^^  ^^  ^ Length of the replacement
     //|                    |             | Index from the string, where it should replace
     //|                    | Get the replaced character to replace it
     //| Replaces every single character one by one in the string 

For more information about substr_replace() see the manual: http://php.net/manual/en/function.substr-replace.php

After this line you just add the replaced string in the result array and rest the string to the source string again.


Code:

<?php

    //data
    $str = "tazeki"; 

    $replace = array(
        'a' => 'ą',
        'e' => 'ę',
        'i' => 'į',
    );


    function getReplacements($array) {

        //initalize array
        $results = [[]];

        //get all combinations
        foreach ($array as $k => $element) {
            foreach ($results as $combination)
                $results[] = [$k => $element] + $combination;
        }

        //return filtered array
        return array_values(array_filter($results));

    }

    //get all combinations to replace
    $combinations = getReplacements(array_intersect(str_split($str), array_keys($replace)));

    //replace all combinations
    foreach($combinations as $word) {
        $tmp = $str;
        foreach($word as $k => $v)
            $tmp = substr_replace($tmp, $replace[$v], $k, 1);
        $result[] = $tmp;
    }

    //print data
    print_r($result);

?>

Output:

Array
(
    [0] => tązeki
    [1] => tazęki
    [2] => tązęki
    [3] => tazekį
    [4] => tązekį
    [5] => tazękį
    [6] => tązękį
)
Rizier123
  • 58,877
  • 16
  • 101
  • 156
3

Here is a solution particularly for your task. You can pass any word and any array for replacements, it should work.

<?php

function getCombinations($word, $charsReplace)
{
    $charsToSplit = array_keys($charsReplace);

    $pattern = '/('.implode('|', $charsToSplit).')/';

    // split whole word into parts by replacing symbols
    $parts = preg_split($pattern, $word, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);

    $replaceParts = array();
    $placeholder = '';

    // create string with placeholders (%s) for sptrinf and array of replacing symbols
    foreach ($parts as $wordPart) {
        if (isset($charsReplace[$wordPart])) {
            $replaceParts[] = $wordPart;
            $placeholder .= '%s';
        } else {
            $placeholder .= $wordPart;
        }
    }

    $paramsCnt = count($replaceParts);
    $combinations = array();
    $combinationsCnt = pow(2, $paramsCnt);

    // iterate all combinations (with help of binary codes)
    for ($i = 0; $i < $combinationsCnt; $i++) {
        $mask = sprintf('%0'.$paramsCnt.'b', $i);
        $sprintfParams = array($placeholder);
        foreach ($replaceParts as $index => $char) {
            $sprintfParams[] = $mask[$index] == 1 ? $charsReplace[$char] : $char;
        }
        // fill current combination into placeholder and collect it in array
        $combinations[] = call_user_func_array('sprintf', $sprintfParams);
    }

    return $combinations;
}


$lt = array(
    'a' => 'ą',
    'e' => 'ę',
    'i' => 'į',
);
$word = 'stazeki';

$combinations = getCombinations($word, $lt);

print_r($combinations);

// Оutput:
// Array
// (
//     [0] => stazeki
//     [1] => stazekį
//     [2] => stazęki
//     [3] => stazękį
//     [4] => stązeki
//     [5] => stązekį
//     [6] => stązęki
//     [7] => stązękį
// )
Yuriy Novikov
  • 321
  • 1
  • 7
1

This is an implementation in PHP :

<?php
/**
 * String variant generator
 */
class stringVariantGenerator
{
    /**
     * Contains assoc of char => array of all its variations
     * @var array
     */
    protected $_mapping = array();

    /**
     * Class constructor
     * 
     * @param array $mapping Assoc array of char => array of all its variation
     */
    public function __construct(array $mapping = array())
    {
        $this->_mapping = $mapping;
    }

    /**
     * Generate all variations
     * 
     * @param string $string String to generate variations from 
     * 
     * @return array Assoc containing variations
     */
    public function generate($string) 
    {
        return array_unique($this->parseString($string));
    }

    /**
     * Parse a string and returns variations
     * 
     * @param string $string String to parse
     * @param int $position Current position analyzed in the string
     * @param array $result Assoc containing all variations
     * 
     * @return array Assoc containing variations
     */
    protected function parseString($string, $position = 0, array &$result = array()) 
    {
        if ($position <= strlen($string) - 1)
        {
            if (isset($this->_mapping[$string{$position}]))
            {
                foreach ($this->_mapping[$string{$position}] as $translatedChar)
                {
                    $string{$position} = $translatedChar;
                    $this->parseString($string, $position + 1, $result);
                }
            }
            else
            {
                $this->parseString($string, $position + 1, $result);
            }
        }
        else
        {
            $result[] = $string;
        }

        return $result;
    }
}

// This is where you define what are the possible variations for each char
$mapping = array(
    'e' => array('#', '_'),
    'p' => array('*'),
);

$word = 'Apple love!';
$generator = new stringVariantGenerator($mapping);
print_r($generator->generate($word));

It would return :

Array
(
    [0] => A**l# lov#!
    [1] => A**l# lov_!
    [2] => A**l_ lov#!
    [3] => A**l_ lov_!
)

In your case, if you want to use the letter itself as a valid translated value, just add it into the array.

$lt = array(
    'a' => array('a', 'ą'),
    'e' => array('e', 'ę'),
    'i' => array('i', 'į'),
);
alfallouji
  • 1,160
  • 8
  • 12
0

I'm not sure if you can do this with keys and value but as two arrays definatley.

$find = array('ą','ę','į');
$replace = array('a', 'e', 'i');
$string = 'tązekį';
echo str_replace($find, $replace, $string);
chris85
  • 23,846
  • 7
  • 34
  • 51
0

I'm not sure If I understand your question, but here is my answer :-)

$word = 'taxeki';
$word_arr = array();
$word_arr[] = $word;

//Loop through the $lt-array where $key represents what char to search for
//$letter what to replace with
//
foreach($lt as $key=>$letter) {

    //Loop through each char in the $word-string
    for( $i = 0; $i <= strlen($word)-1; $i++ ) {
        $char = substr( $word, $i, 1 ); 

        //If current letter in word is same as $key from $lt-array
        //then add a word the $word_arr where letter is replace with
        //$letter from the $lt-array
        if ($char === $key) {
            $word_arr[] = str_replace($char, $letter, $word);
        }

    } 

}

var_dump($word_arr);
bestprogrammerintheworld
  • 5,417
  • 7
  • 43
  • 72
0

I'm assuming you have a known number of elements in your array, and I am assuming that that number is 3. You will have to have additional loops if you have additional elements in your $lt array.

$lt = array(
   'a' => array('a', 'x'),
   'e' => array('e', 'x'),
   'i' => array('i', 'x')
);
$str = 'tazeki';
foreach ($lt['a'] as $a)
    foreach ($lt['e'] as $b)
        foreach ($lt['i'] as $c) {
            $newstr = str_replace(array_keys($lt), array($a, $b, $c), $str);
            echo "$newstr<br />\n";
        }

If the number of elements in $lt is unknown or variable then this is not a good solution.

Peter Bowers
  • 3,063
  • 1
  • 10
  • 18
0

Well, though @Rizier123 and others have already provided good answers will clear explanations, I would like to leave my contribution as well. This time, honoring the Way of the Short Source Code over readability ... ;-)

$lt   = array('a' => 'ą', 'e' => 'ę', 'i' => 'į');
$word = 'tazeki';

for ($i = 0; $i < strlen($word); $i++)
    $lt[$word[$i]] && $r[pow(2, $u++)] = [$lt[$word[$i]], $i];

for ($i = 1; $i < pow(2, count($r)); $i++) {
    for ($w = $word, $u = end(array_keys($r)); $u > 0; $u >>= 1)
        ($i & $u) && $w = substr_replace($w, $r[$u][0], $r[$u][1], 1);
    $res[] = $w;
}

print_r($res);

Output:

Array
(
    [0] => tązeki
    [1] => tazęki
    [2] => tązęki
    [3] => tazekį
    [4] => tązekį
    [5] => tazękį
    [6] => tązękį
)
mhall
  • 3,671
  • 3
  • 23
  • 35
  • I think you didn't had error reporting on: `Undefined variable t,u,z,k` and a lot of `Strict Standards: Only variables should be passed by reference` – Rizier123 Mar 01 '15 at 14:36
  • Yeah @Rizier123, I would not recommend anyone to use it in production even though the result comes out right. Coming from a Perl background I just couldn't resist some code golfing :-P – mhall Mar 01 '15 at 15:01