Rotate letters in a string so that each letter is shifted to another letter in n places

I have been tasked with coming up with a way to encode a string. By the way, I need to shift each letter by a given number, but the converted letter must be a letter (circular shift).

I have the following code:

def play_pass(str, n)
  letters = ('a'..'z').to_a
  str.chars.map {|x| letters.include?(x.downcase) ? (x.ord + n).chr : x}.join
end

      

This works for most letters

My problem is that if I try to shift y

2 places I should get a

, but instead I get the character[

Where am I going wrong?

+3


source to share


4 answers


Try the following:

def play_pass(str, n)
  letters = ('a'..'z').to_a
  str.chars.map {|x| letters.include?(x.downcase) ? 
     letters[letters.find_index(x.down_case) + n - letters.size] : x}.join
end

p play_pass("abcdefghijklmnopqrstuvwxyz", 2)

      

Output

"cdefghijklmnopqrstuvwxyzab"
[Finished in 0.3s]

      



How it works

letters

is an array of a

to characters z

just as the OP has in his code. We iterate over all the characters in str

and find its index in the array letters

. Then we add n

to that index to get the shifted character. To avoid dropping the array, we subtract letters.size

(in this case 26

), so our search in letters

is performed using the value between 0

and 25

.

For example, in a scenario that pointed OP, if the character that you want to move, was y

, then add 2 to its index to letters

give us shifted index 26

( 24

- index y

in the array letters

, 2

- a numeric code that we change in the test case). To letters

behave like a circular array and not collide with the index from the associated exception type, we subtract letters.size

from the 26

shifted index. This way we get the index 0

that the char represents, which is a

what we're interested in.

Another example is the case a

. Here the shifted index will be 0 + 2 = 2

. When we subtract from it letters.size

, we get -24

. Ruby allows negative indices where an array element is searched for using the inverse, and it will fix the fix of the element. The index is the -1

same as the index (size-1)

, similarly, the index value is -size

equal to the index 0

.

+3


source


Your mistake is that you are just blindly changing characters and not using an array letters

to wrap.

def play_pass(str, n)
  letters = ('a'..'z').to_a

  str.chars.map do |x| 
    if letters.include?(x.downcase)
      idx = letters.index(x)
      new_idx = (idx + n) % letters.length
      letters[new_idx]
    else
      x
    end
  end.join

end

play_pass('ay1', 2) # => "ca1"

      



The key to success here is the modulo operator (%) . We use to get the wildcard index from the array letters

. This ensures that the index will always be within the array. Instead of walking past the end, it wraps around.

Read it. This is useful in many places.

+3


source


Arrays have a method rotate

:

def play_pass(str,n)
  abc = ("a".."z").to_a.join
  abc_rot = abc.chars.rotate(n).join
  str.tr(abc, abc_rot)
end

p play_pass("abcdefghijklmnopqrstuvwxyz", 2)
# => "cdefghijklmnopqrstuvwxyzab"

      

The negative one n

rotates the other way.

+3


source


If your code goes wrong when the shifted letter has a smaller character code than its original, but your code always adds a positive value for the shift. You first need to determine when you are in this situation (for example, if the shifted letter is greater than z

), and then do the appropriate fix (hint: consider the difference between the desired character code and that, now).

+2


source







All Articles