Reputation: 219
From a string, I am trying to replace each character with another character, 13 letters ahead in the alphabet. For example, "ABC" would return "NOP".
However, when I get to the end of the alphabet, I can't find a way to loop the index, so that it goes from index [-1]
to [0]
again and onwards. For example - When I type in "XYZ" it returns "", but it should return "KLM".
Can anyone think of a solution to this?
def rot13(string)
alphabet = ("a".."z").to_a
big_alphabet = ("A".."Z").to_a
result = []
split_string = string.chars
alphabet.each_with_index do |item,index|
if string.include?(alphabet[index])
result << alphabet[index+13]
elsif string.include?(big_alphabet[index])
result << big_alphabet[index+13]
end
end
return result.join
end
Upvotes: 1
Views: 2543
Reputation: 11892
You can build a hash first, mapping a
to n
, n
to a
, A
to N
, N
to A
, etc.
Then you simply remap your letters using this hash.
def remap input
a1 = ("a".."m").to_a
a2 = ("n".."z").to_a
a3 = ("A".."M").to_a
a4 = ("N".."Z").to_a
letter_map = (a1 + a2 + a3 + a4).zip(a2 + a1 + a4 + a3).to_h
input.split('').map {|x| letter_map[x] || x}.join
end
If there's no mapping for the letter (eg space, punctuation, digits), you simply use the original letter.
2.2.1 :125 > remap 'john smith'
=> "wbua fzvgu"
2.2.1 :126 > remap 'i love you!'
=> "v ybir lbh!"
Upvotes: 1
Reputation: 114188
Based on your "source" alphabet:
alphabet = ('a'..'z').to_a
#=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
# "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
You could use Array#rotate
to create the "destination" alphabet:
alphabet13 = alphabet.rotate(13)
#=> ["n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
# "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m"]
Build a replacement hash by zip
-ing both arrays:
replacement = alphabet.zip(alphabet13).to_h
#=> {"a"=>"n", "b"=>"o", "c"=>"p", "d"=>"q", "e"=>"r", "f"=>"s", "g"=>"t",
# "h"=>"u", "i"=>"v", "j"=>"w", "k"=>"x", "l"=>"y", "m"=>"z",
# "n"=>"a", "o"=>"b", "p"=>"c", "q"=>"d", "r"=>"e", "s"=>"f", "t"=>"g",
# "u"=>"h", "v"=>"i", "w"=>"j", "x"=>"k", "y"=>"l", "z"=>"m"}
And use gsub
to perform the substitution:
'abc xyz'.gsub(/[a-z]/, replacement)
#=> "nop klm"
You can also use Regexp.union(replacement.keys)
instead of the hard coded /[a-z]/
.
Replacing the uppercase characters can be done in a separate step or by incorporating them into the replacement hash. I leave that to you.
Upvotes: 2
Reputation: 230386
Here's the simplest thing that can work (utilizing the little-used String#tr
). Gracefully handles non-letters too.
def rot13(str)
alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
replacements = 'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
str.tr(alphabet, replacements)
end
rot13('abc') # => "nop"
rot13('nOp 123') # => "aBc 123"
Upvotes: 2
Reputation: 1115
You could do this
def rot13(string)
string.each_codepoint.map { |c|
new_c = c + 13 # Increment in alphabets
if ((c.between?('a'.ord, 'z'.ord) && new_c > 'z'.ord) || (c.between?('A'.ord, 'Z'.ord) && new_c > 'Z'.ord))
new_c -= 26 # This would keep the character within desired range
end
new_c.chr
}.join('')
end
Upvotes: 0
Reputation: 168121
I don't understand your entire code, but if that works for the beginning of the alphabets, then you should perhaps change all index + 13
to (index + 13) % 26
, and it should work.
Upvotes: 2