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

前端 未结 4 1542
無奈伤痛
無奈伤痛 2021-01-05 20:07

I have been tasked with coming up with a way of encoding a string. Among other things, I need to shift each letter by a given number but the transformed letter must be a let

相关标签:
4条回答
  • 2021-01-05 20:40

    Try this:

    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 chars a to z just the way OP has in his code. We iterate over all chars in str, and find its index in letters array. Then we add n to that index to get the shifted character. To avoid falling off the array, we subtract letters.size (in this case 26), so that the our lookup into letters is done using value between 0 and 25.

    For example: In the scenario that OP pointed out, if the character to be shifted was y, then, adding 2 to its index in letters will give us shifted index 26 (24 is index of y in letters array, 2 is number characters we are shifting in the test case) - To make letters behave like circular array, and not encounter index out of bound type of exception, we subtract letters.size from 26 shifted index. Thus, we get index 0, which represents char a which is what we are interested in.

    Another example is case of a - Here the shifted index will be 0 + 2 = 2. When we subtract letters.size from it, we get -24. Ruby allows negative indexes wherein lookup of array element is done from reverse, and it will resolve to correct element. Index -1 is same as Index (size-1), similarly, index value of -size is equal to index 0.

    0 讨论(0)
  • 2021-01-05 20:43

    Your mistake is that you just blindly shift characters and don't use your letters array to wrap around.

    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 an index of a substitution character from letters array. It guarantees that the index will always be within bounds of the array. Instead of going past the end, it wraps around.

    Read up on it. It's useful in many places.

    0 讨论(0)
  • 2021-01-05 20:58

    Where your code goes wrong is when the shifted letter has a smaller character code than its original was, yet your code always adds a positive value to do the shift. You first need to detect when you are in this situation (for example, if the shifted letter is greater than z), and then make the appropriate fix (hint: consider the difference between the character code you want and the one you are getting now).

    0 讨论(0)
  • 2021-01-05 21:00

    Arrays have a rotate method:

    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"
    

    A negative n rotates the other way.

    0 讨论(0)
提交回复
热议问题