0

I need to change each character in a string but this change is include all possible results. Keys and Values of array variable can replace each other

for example list of all letter is similar to below

$changeList=array(
"ş"=>"s",
"ç"=>"c",
"i"=>"ı",
"ğ"=>"ğ",
"ü"=>"u",
"ö"=>"o",
"Ş"=>"S",
"Ç"=>"C",
"İ"=>"I",
"Ğ"=>"G",
"Ü"=>"U",
"Ö"=>"O"
);

$word="ağaç";

if I calculated correct , this is posible 2^2 = 4 results. (for ğ and ç letters) after replace all letters , I need this results.

  1. ağaç
  2. agaç
  3. ağac
  4. agac

and other words example =pişir this words include changeable three letter. 2^3 = 8 results. (for s,ı,ç 3 letters)

  1. pişir
  2. pışir
  3. pisir
  4. pişır
  5. pişır
  6. pisır
  7. pısır
  8. pışır

I need an array variable which contains all results. How can I do this , with best performance.

sfk
  • 81
  • 7
  • 1
    What is your question? I have no clue what you are asking – putvande Nov 22 '13 at 15:32
  • I need a function when i give two arguments , word and changelist (array) function replace change list letter in word and after each replace add word an array variable and return me a array variable which contains all result. sorry my english is not good – sfk Nov 22 '13 at 15:57
  • I can think of an implementation that *almost* answers your question, but I note there is a specific requirement that `pişir` illustrates: you want to generate a new word version for each individual letter change - **including** letters that occur more than once. This complicates matters somewhat in that you'll have to analyse each letter in your target word, for starters... Not very efficient I suspect. If multiple instances of a letter in a word could be converted in one pass it would be a lot simpler, with functions like `strtr` for instance, but it's not. – Darragh Enright Nov 22 '13 at 16:24
  • it's also complicated by the presence of multibyte characters... – Darragh Enright Nov 22 '13 at 17:12

1 Answers1

1

Okay, I became a little obsessed with this and decided to have a go (hey, it's Friday evening).

As I noted in the comment above, you want to generate a new word variation for each individual letter change - including letters that occur more than once. Another tricky consideration is multibyte characters.

What follows is an attempt an an answer that is probably pretty sub-optimal but hopefully gets the job done... I'm sure there are many ways to improve this but it's a start that you might be able to iterate on:

<?php

// this solution assumes UTF-8 config settings in php.ini
// setting them with ini_set() seems to work also but ymmv
ini_set('default_charset', 'UTF-8');
ini_set('mbstring.internal_encoding', 'UTF-8');

function getWordReplacements($word, $replacements) {

    // $results... add the original
    $results = array($word);

    // multibyte split of $word
    // credit: http://stackoverflow.com/a/2556753/312962
    $chars = preg_split('//u', $word, -1, PREG_SPLIT_NO_EMPTY); 

    // we will iterate over the chars and create replacements twice,
    // first pass is a forward pass, second is a reverse pass
    // we need a copy of the $chars array for each pass
    $forwardPass = $chars;
    $reversePass = $chars;

    // how many chars in the word?
    $length = count($chars);
    // we'll store the index of the first character 
    // that has a copy in the $replacements list for later
    $first  = null;

    // first pass... iterate!
    for ($i = 0; $i < $length; $i++) {
        // is the current character in the $replacements list?
        if (array_key_exists($chars[$i], $replacements)) {
            // save the index of the first match
            if (is_null($first)) {
                $first = $i;
            }
            // replace the found char with the translation
            $forwardPass[$i] = $replacements[$forwardPass[$i]];
            // flatten the result and save it as a variation
            $results[] = implode($forwardPass);
        }
    }

    // second pass... same as the first pass except:
    // * we go backwards through the array
    // * we stop before we reach the $first index, to avoid a duplicate
    //   entry of the first variation saved in the first pass...
    for ($j = ($length - 1); $j > $first; $j--) {
        if (array_key_exists($chars[$j], $replacements)) {
            $reversePass[$j] = $replacements[$reversePass[$j]];
            $results[] = implode($reversePass);
        }
    }

    // return the results
    return $results;
}

// test with a subset replacement list
$list = [
    'ç' => 'c',
    'ğ' => 'g',
    'ş' => 's',
    'i' => 'ı',
];

// a couple of examples
$r1 = getWordReplacements('pişir', $list);
$r2 = getWordReplacements('ağaç', $list);

var_dump($r1, $r2);

Yields:

array (size=6)
  0 => string 'pişir' (length=6)
  1 => string 'pışir' (length=7)
  2 => string 'pısir' (length=6)
  3 => string 'pısır' (length=7)
  4 => string 'pişır' (length=7)
  5 => string 'pisır' (length=6)
array (size=4)
  0 => string 'ağaç' (length=6)
  1 => string 'agaç' (length=5)
  2 => string 'agac' (length=4)
  3 => string 'ağac' (length=5)

Hope this helps :)

Darragh Enright
  • 13,676
  • 7
  • 41
  • 48