0

In PHP, we know that we can randomly shuffle string characters using function str_shuffle(). So a string "developer" will become "lrevdeope", "dvolpeere" and so on every time.

But this is not the thing I want. Instead, I want to randomly shuffle only consonants. So "developer" should become "verelodep", "leveroped" etc. on each page refresh.

How can we achieve it? Any idea?

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Sachin
  • 1,646
  • 3
  • 22
  • 59

4 Answers4

3

After couple of minutes I have this:

$s = 'developer';
$cons = [];
for ($i = 0; $i < strlen($s); $i++) {
    if (!in_array($s[$i], ['a', 'e', 'i', 'o', 'u', 'y'])) {
        $cons[] = $s[$i];
        $s[$i] = '-';
    }
}

shuffle($cons);

for ($i = 0; $i < strlen($s); $i++) {
    if ($s[$i] == '-') {
        $s[$i] = array_shift($cons);
    }
}

echo $s . PHP_EOL;
u_mulder
  • 54,101
  • 5
  • 48
  • 64
  • @u_mulder please cache the `strlen()`. Good answer. What about `strpos()` instead of `in_array()`? Perhaps faster. – mickmackusa Feb 11 '18 at 10:29
  • 1
    ...and as a matter of good role modeling, please include some explanation with your code. – mickmackusa Feb 11 '18 at 10:55
  • 1
    @mickmackusa Complexity of `strlen` is O(1), there's no need to cache anything. I don't think that `strpos` will significantly speed up this code. – u_mulder Feb 11 '18 at 10:57
  • It works, so obviously it is _not needed_. How about as a matter of "don't repeat yourself" / "don't ask php to repeat function calls"? – mickmackusa Feb 11 '18 at 10:58
0

You can create custom function like this:

function shuffle_consonants($str) {
  $str = str_split($str);
  $vowels = ['a','e','i','o','u'];

  foreach($str as $char) 
      if(!in_array($char, $vowels)) $con .= $char;

  $con = str_shuffle($con);
  $idx = 0;

  foreach($str as &$char) 
      if(!in_array($char, $vowels)) $char = $con[$idx++];

  return implode("", $str);
}
echo shuffle_consonants("developer");
Khaled Alam
  • 885
  • 7
  • 12
  • Smashing all of your declarations into one line does not improve readability. Furthermore, code-only answers are low value on StackOverflow. – mickmackusa Feb 11 '18 at 10:25
  • difficult to follow due to poor indentation and without any line break! – Sachin Feb 11 '18 at 10:27
  • Now, please edit your answer with the intent to educate future readers. – mickmackusa Feb 11 '18 at 10:30
  • You have not explained your method. Please continue to improve your post. Providing a correct method is only the beginning of a good post on StackOverflow. – mickmackusa Feb 11 '18 at 10:48
0

Well here's an alternative for you:

$word = "developer";

$letters = str_split($word);

$consonants = array_filter($letters, function ($letter) {
    return !in_array($letter, ['a', 'e', 'i', 'o', 'u', 'y']);
}); //Get all consonants with their index

$consonantPositions = array_keys($consonants); //keep indexes (shuffle will lose them)

shuffle($consonants);

$letters = array_combine($consonantPositions, $consonants) + $letters; // put the shuffled consonants in their indexes and glue them back into the letters

ksort($letters); // put the keys back in their place

echo implode("",$letters); 
apokryfos
  • 38,771
  • 9
  • 70
  • 114
  • I wrote a near duplicate of this (didn't post https://3v4l.org/oRkZA ). Please implement `array_diff()` instead of filter. `implode()` doesn't need a glue parameter when empty. – mickmackusa Feb 11 '18 at 10:50
0

I appreciate the over-arching design of u_mulder's method, but I would like to make some refinements/micro-optimizations for any readers who may be interested.

  • store the strlen() value so that php doesn't have to repeatedly generate it.
  • call strpos() to differentiate vowels from consonants. (Ref: in_array vs strpos for performance in php)

  • do not temporarily replace consonants in the input string with -.

  • iterate only the consonants in the second loop (versus iterating all of the letters in the string).
  • make replacements on the string without a function call.

Code: (Demo)

$string="developer";
$consonants=[];
$length=strlen($string);
for($offset=0; $offset<$length; ++$offset){  // iterate each letter of the string           ... OR for($offset=strlen($string); --$offset;){
    if(strpos('aeiou',$string[$offset])===false){  // isolate the consonants
        $consonants[]=$string[$offset];  // store the consonant
        $offsets[]=$offset;  // store the offset (aka indexed position of the consonant in the string)
    }
}
shuffle($consonants);  // shuffle the array of stored consonants

foreach($consonants as $index=>$consonant){  // iterate ONLY the stored consonants
    $string[$offsets[$index]]=$consonant;  // reassign the consonants in their new positions
}

echo $string;  // possible output: revepoled

And here is a blend of array functions with a foreach loop to re-insert the shuffled consonants:

$string="developer";
$consonants=array_diff(str_split($string),['a', 'e', 'i', 'o', 'u']);  // isolate consonants, preserve offsets as keys
$offsets=array_keys($consonants); // store copy of offsets before shuffling
shuffle($consonants);  // shuffle the array of stored consonants (returned value is re-indexed)

foreach($consonants as $i=>$consonant){
    $string[$offsets[$i]]=$consonant;  // reassign the shuffled consonants at the known consonant positions
}

echo $string;

For anyone who thinks I don't have any independent ideas to offer... Here is another approach that will implements two string function calls followed by a regex function call (which will negatively impact speed, but not horribly) that may be written as a two-liner.

Code: (Demo)

$word="developer";

$shuffled_consonants=str_shuffle(str_replace(['a','e','i','o','u'],'',$word));  // generate shuffled string of consonants

// reinsert shuffled consonants at original consonant positions
echo preg_replace_callback(
    '~[^aeiou]~',                                 // match each consonant at original position
    function($m)use($shuffled_consonants){        // pass in the shuffled string
        static $offset=0;                         // init the offset counter
        return $shuffled_consonants[$offset++];   // insert new consonant at original position using post-incrementation
    },
    $word);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136