chrabyrd
chrabyrd

Reputation: 323

I changed my integers to strings, but #gsub only recognizes 0-9

I'm self-learning Ruby, and one assignment is to make a Caesar cipher. Using #gsub, I've been able to change my letters to integers ('c' => 2), shift them, then change the new integers to strings (2 => "2").

I've hit a wall, and the Ruby documentation isn't helping. When I try to #gsub the strings back to letters ("2" => 'c') it only recognizes 0-9. Everything after that is just a concatenation of those numbers ("12" => 'bc' instead of => 'l').

Why does Ruby do this, and how can I fix it?

Thanks for your help guys.

code: (I know it's sloppy beginner's code; I will try to edit it after it passes)

def convert_to_integer
    puts "What would you like to encode?"
    words = gets.chomp

    words = words.split("")
    words.map { |words| words.gsub!(/[a-z]/, 'a' => 0, 'b' => 1, 'c' => 2, 'd' => 3, 'e' => 4, 'f' => 5, 'g' => 6, 'h' => 7, 'i' => 8, 'j' => 9, 'k' => 10, 'l' => 11, 'm' => 12, 'n' => 13, 'o' => 14, 'p' => 15, 'q' => 16, 'r' => 17, 's' => 18, 't' => 19, 'u' => 20, 'v' => 21, 'w' => 22, 'x' => 23, 'y' => 24, 'z' => 25)
    }
    integer = words.map! { |letter| letter.to_i }

    return integer
end

def shift_left(integer, number = 0)
    puts "How many letters (to the left) would you like to shift it?"
    number = gets.to_i

    integer.map! { |n| n - number }

    return integer
end

def convert_to_letter(integer)
    integer.map! { |integer| integer.to_s }

    integer.map! { |n| n.gsub(/[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 , 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]/, '0' => 'a', '1' => 'b', '2' => 'c', '3' => 'd', '4' => 'e', '5' => 'f', '6' => 'g', '7' => 'h', '8' => 'i', '9' => 'j', '10' => 'k', '11' => 'l', '12' => 'm', '13' => 'n', '14' => 'o', '15' => 'p', '16' => 'q', '17' => 'r', '18' => 's', '19' => 't', '20' => 'u', '21' => 'v', '22' => 'w', '23' => 'x', '24' => 'y', '25' => 'z')
    }
    print integer
end
convert_to_letter(shift_left(convert_to_integer))    

Upvotes: 1

Views: 827

Answers (3)

joelparkerhenderson
joelparkerhenderson

Reputation: 35483

It's easier and faster to use lookups:

@letter_to_number = ('a'..'z').zip(0..25).to_h
@number_to_letter = (0..25).zip('a'..'z').to_h

def convert_to_integers(letters)
  letters.map{|l| @letter_to_number[l]}
end

def convert_to_letters(numbers)
  numbers.map{|n| @number_to_letter[n]}
end

There's also a shortcut that combines the lookups and combines the methods.

@convert = (('a'..'z').zip(0..25) + (0..25).zip('a'..'z')).to_h

def convert(objects)
  objects.map{|o| @convert[o]}
end

Upvotes: 1

Amit Kumar Gupta
Amit Kumar Gupta

Reputation: 18597

That's not how regular expressions work. "12".gsub(/[12]/, '12' => 'm') does not produce "m". That code says to find any occurrence of "1" or "2", and replace it according to the following rule: "12" gets replaced with "m", and, implicitly, anything else gets replaced with nothing. Both the "1" and the "2" are occurrences of "1" or "2", but neither of them are "12", so they both get replaced with nothing. Thus the above results in just the empty string.

In fact gsub and regular expressions are not really ideal for this problem. You could just do this:

def char_to_int(char)
    char.ord - 97
end

def int_to_char(int)
    (int + 97).chr
end

def caesar(string, shift)
  string.split(" ").map do |word| 
    word.split("").map do |letter|
      int_to_char((char_to_int(letter) - shift) % 26)
    end.join
  end.join(" ")
end

Upvotes: 0

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230481

You don't need to do a gsub there. gsub is normally used to replace parts of a bigger string. You want to replace the whole thing.

This should do the trick:

def convert_to_letter(integers)
  replacements = {0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd', 4 => 'e', 
    5 => 'f', 6 => 'g', 7 => 'h', 8 => 'i', 9 => 'j', 10 => 'k', 
    11 => 'l', 12 => 'm', 13 => 'n', 14 => 'o', 15 => 'p', 16 => 'q', 
    17 => 'r', 18 => 's', 19 => 't', 20 => 'u', 21 => 'v', 22 => 'w', 
    23 => 'x', 24 => 'y', 25 => 'z'  
  }

  integers.map{|x| replacements[x]}.join
end

Also, be careful with destructive operations (map! here). You may run into undesired side-effects (for example, some arrays will change when you think they shouldn't).

Upvotes: 2

Related Questions