Gcap
Gcap

Reputation: 378

Ruby error - '+': no implicit conversion of Fixnum into String (TypeError)

This line is throwing the error:

charLine.each_index { |i| charLine[i] = (charLine[i] + shift)}

edit: charLine[i] is a number (ascii code) and shift is a number also

I know it has to do with trying to add the number in the variable shift to the byte ascii code I have in the charLine array index. But I'm not even dealing with strings at all which is why I'm so confused with this error... Here's the method with the error:

      def caesarCipher(string)
    puts "Original:\n #{string}"
    charLine = string.chars
    charLine.each_index { |i| charLine[i]=charLine[i].bytes }
    charLine.flatten!
    puts charLine.inspect

    shift = 1
    alphabet = ('A'..'Z').to_a
    while shift <= alphabet.size
      #moving up in the alphabet using ascii code
      charLine.each_index { |i| charLine[i] = (charLine[i] + shift)}
      #converting back to letters
      charLine.each_index { |x| charLine[x] = charLine[x].chr }

      puts "Shifted:\n #{charLine.inspect}"
      puts "With a letter shift of #{shift}"
      shift = shift + 1
      @@shiftyArray.push(charLine.flatten)
    end

  end

Upvotes: 0

Views: 2252

Answers (2)

Jesus Castello
Jesus Castello

Reputation: 1113

I have managed to reproduce the problem and with some debugging output I can see that the first iteration runs fine and the problem only appears on the second iteration (shift = 2).

In the first iteration charLine is an array of integers, so integer + integer works out fine for this line: charLine.each_index { |i| charLine[i] = (charLine[i] + shift)}

But then charLine is converted into an array of strings on the next line of the loop:

 #converting back to letters
 charLine.each_index { |x| charLine[x] = charLine[x].chr }

So at the start of second iteration charLine is now an array of Strings, because it wasn't converted back. Then on the .each_line it tries to add up string + integer and it blows up.

The solution is to re-map your string array to integer at the start of every iteration.

charLine.map!(&:ord)

The alternative is to not modify the array and save the results into a temp variable, here is a working example:

def caesar_cipher(string)
   shiftyArray = []
   charLine = string.split(//)
   charLine.map!(&:ord)

   shift = 1
   alphabet_size = 26
   while shift <= alphabet_size
     shifted_array = charLine.map { |c| (c + shift) < 122 ? (c + shift) : (c + shift) - 26 }
     shifted_array.map!(&:chr)
     p shifted_array

     shift += 1
     shiftyArray.push(shifted_array.join)
   end
 end

 caesar_cipher("testing")

Upvotes: 0

infused
infused

Reputation: 24337

You are trying to add shift, which is a Fixnum to a String. Just call to_s on shift to convert it to a string:

charLine.each_index { |i| charLine[i] = (charLine[i] + shift.to_s)}

Upvotes: 1

Related Questions