How do I shuffle the middle letters of every word in a string?

  • A+

Trying to recreate the "Only smart people can read this" meme. Here's a sample:

Hrad to blveiee taht you cluod aulaclty uesdnatnrd waht yor’ue rdanieg. The phaonmneal pweor of the hmuan bairn, aoccdrnig to a rscheearch at Cmabrigde Uinervtisy, sowhs taht it deosn’t mttaer in waht oredr the ltteers in a wrod are, the olny iprmoatnt tihng is taht the frist and lsat ltteer be in the rghit pclae.

The rset can be a taotl mses and you can sitll raed it wouthit a porbelm.

Tihs is bcuseae the huamn biarn deos not raed ervey lteter by istlef, but the wrod as a wlohe ptatren. Amzanig huh? Yaeh, and you awlyas tghuhot slpeling was ipmorantt!

How do I create a Ruby method that jumbles the middle words for every word greater than 3 letters, in a sentence I pass it.

Clarification: I've posted this question and answer both at the same time. There's no code in the question because I posted it in the answer.

Okay, I'll bite:

def srlabmce(str)   str.gsub(/([/p{L}'])([/p{L}']{2,})([/p{L}'])/) { "#$1#{$2.chars.shuffle.join}#$3" } end  puts srlabmce("Hard to believe that you could actually understand what you're reading") # => Hrad to beviele taht you cuold atlculay unantdresd what yoru'e raeindg 

See it on


I originally used the Regexp /(/S)(/S+)(/S)/, which counted as a "word" any sequence of three or more non-whitespace characters. This unfortunately counted punctuation as word characters, so e.g. "Hello, world." might become "Hlloe, wlodr."—the , and . were counted as the last "letters" of the words, and the actual last letters were moved.

I've updated it to use the Regexp /([/p{L}'])([/p{L}']{2,})([/p{L}'])/. The character class /p{L} corresponds to the Unicode category "Letters," so it works with basic diacritics, and I threw in ' to match amingilani's implementation.

puts srlabmce("Quem ïd feugiat iaculisé éu mié tùrpus ïn interdùm grâvida, malesuada vivamus nam nullä urna justo conubia torétoré lorem.") # => Qeum ïd fgieuat iliacusé éu mié tpùurs ïn iedùtnrm girâdva, madueasla vimavus nam nullä unra jutso cnboiua ttoréroé lerom. 

Update 2

If we want to add the requirement that no word's letter order may be the same in the output as the input, we can modify the proc passed to gsub to call itself again until the order has changed:

def srlabmce(str)   replacer = ->*{     if $2.chars.uniq.size < 2 then $&     else       o = $2.chars.shuffle.join       o == $2 ? replacer[] : "#$1#{o}#$2"     end   }   str.gsub(/([/p{L}'])([/p{L}']{2,})([/p{L}'])/, &replacer) end 

We can still make this a one-liner, but its readability quickly deteriorates:

def srlabmce(str)   str.gsub(/([/p{L}'])([/p{L}']{2,})([/p{L}'])/, &(r = ->*{ $2.chars.uniq.size < 2 ? $& : (o = $2.chars.shuffle.join) == $& ? r[] : "#$1#{o}#$3" })) end 

See it on


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: