Jack_Hardy
Jack_Hardy

Reputation: 219

Method in Ruby to replace string with characters in the alphabet + index 13

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

Answers (6)

Deepak Kumar
Deepak Kumar

Reputation: 41

def rot13(str)    
    str.map{|x| x.tr('a-zA-Z', 'n-za-mN-ZA-M')}   
end

Upvotes: 0

Jon Snow
Jon Snow

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

Stefan
Stefan

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

Sergio Tulentsev
Sergio Tulentsev

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

Rohit Jangid
Rohit Jangid

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

sawa
sawa

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

Related Questions