Home > OS >  How to encrypt string in ruby
How to encrypt string in ruby

Time:04-05

The method below should take an "encrypted" string and return the string "unencrypted", but it is returning the error "undefined method `-' for nil (NoMethodError)" I've tried everything but I can't get beyond that. basically the code is this:

ALPHABET = ('a'..'z').to_a
def decode(string, factor)
    string = string.split(//)
    string = string.each_with_index.map do |char, _|
      if char.match(/\w/).nil?
        char
      else
        ALPHABET[ALPHABET.index(char) - factor]
      end
    end
    string.join
  end


  • string is 'Zd tytxtrzd cpnflclx op Vlcwdcfsp, xld lelnlclx l ntolop gtktysl!'.
  • factor is 11.
  • decoded message must be 'Os inimigos recuaram de Karlsruhe, mas atacaram a cidade vizinha!'


  • But returns

    undefined method `-' for nil:NilClass


    How can I solve this?

  • CodePudding user response:

    You are setting your ALPHABET only with lower case letters (ALPHABET = ('a'..'z').to_a), but in the string, you are using some upper case letters ('Z', 'V'). As these letters won't be on the ALPHABET, the index related to them does not exist and will be nil (e.g ALPHABET.index('Z') is nil).

    In order to solve that, considering that the letters case on the output is not relevant, you can just do a preprocessing on your string converting all the letters to lower case, and you will then get the expected result:

    ALPHABET = ('a'..'z').to_a
    def decode(string, factor)
      string = string.downcase.split(//) # converting into downcase here
      string = string.each_with_index.map do |char, _|
        if char.match(/\w/).nil?
          char
        else
          ALPHABET[ALPHABET.index(char) - factor]
        end
      end
      string.join
    end
    
    string = "'Zd tytxtrzd cpnflclx op Vlcwdcfsp, xld lelnlclx l ntolop gtktysl!'"
    factor = 11
    puts decode(string, factor)
      # => 'os inimigos recuaram de karlsruhe, mas atacaram a cidade vizinha!'
    
    

    UPDATE considering that the case is relevant:

    In this case, you can then define two alphabets (one for lower case and one for upper case), and then use them accordingly. Like this:

    LOWCASE_ALPHABET = ('a'..'z').to_a
    UPCASE_ALPHABET = ('A'..'Z').to_a
    
    def decode(string, factor)
      string = string.split(//) # converting into downcase here
      string = string.each_with_index.map do |char, _|
        if char.match(/\w/).nil?
          char
        else
          # if is a lower case letter, uses lowcase alphabet and, otherwise, uses upcase alphabet
          alphabet = /[a-z]/.match(char) ? LOWCASE_ALPHABET : UPCASE_ALPHABET
          alphabet[alphabet.index(char) - factor]
        end
      end
      string.join
    end
    
    string = "'Zd tytxtrzd cpnflclx op Vlcwdcfsp, xld lelnlclx l ntolop gtktysl!'"
    factor = 11
    puts decode(string, factor)
      # => 'Os inimigos recuaram de Karlsruhe, mas atacaram a cidade vizinha!'
    

    CodePudding user response:

    Another version with one alphabet:

    ALPHABET = ('a'..'z').to_a
    def decode(string, factor)
      string.each_char.map do |char|
        index = ALPHABET.index(char.downcase)
        decoded = index.nil? ? char : ALPHABET[index - factor]
        char == char.downcase ? decoded : decoded.upcase
      end.join
    end
    

    Priority of the code is readability/brevity over speed.

    • Related