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 repl.it: https://repl.it/@jrunning/TrainedDangerousSlope
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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With