Julia Chou

Rot13 in Ruby

Just for fun, I decided to try my hand at implementing a Rot13 program, which is a simple problem which takes a string as its input and encodes it by moving each letter 13 characters forward or backward. For example, since there are 26 letters in the alphabet, we can assign indices to each letter starting at 0 for 'a' and ending at 25 for 'z'. The Rot13 program would switch (or rotate) the character 'a' with 'n' since it has the index 13.

Giving this program the string input 'apple' would (hopefully) return 'nccyr'.

To that end, the following is the first pass I made at implementing the program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Translator
  ALPHABET = 'abcdefghijklmnopqrstuvwxyz'

  def translate(string)
    result = ''
    string.split(//).each do |char|
      if char == ' ' #preserve spaces if string has multiple words
        result << char
        next
      end
      result << translate_char(char)
    end
    result
  end

  private

  def translate_char(char)
    lowercase = char.downcase
    is_uppercase = char != lowercase
    index = ALPHABET.index lowercase
    translated_index = index - 13
    if translated_index < 0
      translated_index += 26
    end

    #preserve case in original string
    if is_uppercase
      ALPHABET[translated_index].upcase
    else
      ALPHABET[translated_index]
    end
  end
end

In this solution, I created a class Translator that defines constant ALPHABET string representing the alphabet and an instance method #translate that takes in a string, splits it into an array of its characters, translates each character using a private method #translate_char, and then returns a string composed of those translated characters.

The private #translate_char method checks the capitalization of the character, checks the index of the character against the ALPHABET constant, and then rotates that character by returning the appropriate character in ALPHABET at that given index minus 13.

All in all, this got the job done.

However, I later discovered Ruby’s String#tr method, which allows you to replace characters in a string. It is similar to the #gsub method, but while #gsub can match complex patterns with complex results, #tr can only replace fixed characters.

In any case, you can call 'hello'.tr('el', 'ip') to get the result hippo since it will replace every 'e' with 'i' and every 'l' with 'p'.

It turns out that the above implementation can be greatly simplified by the following:

1
2
3
4
5
class Translator
  def translate(string)
    string.tr('a-zA-Z', 'n-za-mN-ZA-M')
  end
end

Spaces and punctuation are preserved since it only checks the string for those characters and replaces them accordingly, ignoring all else. I also realized at this point that my original solution would not have accounted for punctuation or special characters and would have stripped them out in the result.

Using the String#tr method, I was able to simplify my ~30 line class down to 5 lines in a more idiomatic (and more effective!) Ruby solution.

Pretty cool.