Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

string

ruby

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.

like image 470
Amin Shah Gilani Avatar asked May 03 '18 19:05

Amin Shah Gilani


2 Answers

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 repl.it: https://repl.it/@jrunning/TrainedDangerousSlope

Update

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}#$3"
    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 repl.it: https://repl.it/@jrunning/TrainedDangerousSlope-2

like image 58
Jordan Running Avatar answered Sep 22 '22 23:09

Jordan Running


Edit: this code now guarantees words can't accidentally be scrambled to their original text. E.g. read will now always be scrambled to raed.

Edit 2: if words can't be scrambled, it'll return the original word, e.g. jumble 'feet' # => 'feet'

Create a method to jumble individual words, and call it via mess_up for each word in the sentence

def mess_up(sentence)
  sentence = sentence.downcase.split(' ').map { |e| jumble(e) }.join(' ')
  sentence[0] = sentence[0].upcase
  sentence
end

def jumble(word)
  return word if word.size <= 3
  str = word.split('')
  f = str.shift
  l = str.pop
  return word if str.uniq.size == 1
  str = [f, str.shuffle, l].join('')
  return jumble(str) if word == str
  str
end

mess_up "Hard to believe that you could actually understand what you're reading"
# => "Hrad to bleevie taht you cuold aactlluy unrdnestad waht y'ruoe rendaig"

Motivation

I did this as a fun experiment when I saw the post. Was going to push it in a Gist, but realized someone may search for this at some point and SO is the best place for it.

like image 31
Amin Shah Gilani Avatar answered Sep 21 '22 23:09

Amin Shah Gilani