Rotating letters in a string so that each letter is shifted to another letter by n places
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
.
How to shift each letter of the string by a given number of letters?
Do you mean something like ROT13:
pax$ echo 'hello there' | tr '[a-z]' '[n-za-m]'
uryyb gurer
pax$ echo 'hello there' | tr '[a-z]' '[n-za-m]' | tr '[a-z]' '[n-za-m]'
hello there
For a more general solution where you want to provide an arbitrary rotation (0 through 26), you can use:
#!/usr/bin/bash
dual=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
phrase='hello there'
rotat=13
newphrase=$(echo $phrase | tr "${dual:0:26}" "${dual:${rotat}:26}")
echo ${newphrase}
JS: how to shift each letter in the given string N places down in the alphabet?
You need to pass an argument to the fromCharCode method using the String object. Try:
function CaesarCipher(str, num) { // you can comment this line str = str.toLowerCase();
var result = ''; var charcode = 0;
for (var i = 0; i < str.length; i++) { charcode = (str[i].charCodeAt()) + num; result += String.fromCharCode(charcode); } return result;
}console.log(CaesarCipher('test', 2));
Function That Receives and Rotates Character - Caesar Cipher
def rotate(letter, rot):
shift = 97 if letter.islower() else 65
return chr((ord(letter) + rot - shift) % 26 + shift)
letter = input('Enter a letter: ')
rot = int(input('Enter a number: '))
print(rotate(letter, rot))
Crazy Challenge: Rotating Letters in Python Encipher Function
Your problem is here:
new_ord = new_ord - (2*n)
The idea is that when you go past z, you have to remove exactly the whole alphabet and not remove twice what you just added.
Try:
new_ord = new_ord - 26
Ruby Cyphering Leads to non Alphanumeric Characters
The reason you get the verbose output is because Ruby is running with UTF-8 encoding, and your conversion has just produced gibberish characters (an invalid character sequence under UTF-8 encoding).
ASCII characters A-Z
are represented by decimal numbers (ordinals) 65-90, and a-z
is 97-122. When you add 127 you push all the characters into 8-bit space, which makes them unrecognizable for proper UTF-8 encoding.
That's why Ruby inspect
outputs the encoded strings in quoted form, which shows each character as its hexadecimal number "\xC7..."
.
If you want to get some semblance of characters out of this, you could re-encode the gibberish into ISO8859-1, which supports 8-bit characters.
Here's what you get if you do that:
s = "\xC7\xE4\xEB\xEB\xEE \xF6\xEE\xF1\xEB\xE3!"
>> s.encoding
=> #<Encoding:UTF-8>
# Re-encode as ISO8859-1.
# Your terminal (and Ruby) is using UTF-8, so Ruby will refuse to print these yet.
>> s.force_encoding('iso8859-1')
=> "\xC7\xE4\xEB\xEB\xEE \xF6\xEE\xF1\xEB\xE3!"
# In order to be able to print ISO8859-1 on an UTF-8 terminal, you have to
# convert them back to UTF-8 by re-encoding. This way your terminal (and Ruby)
# can display the ISO8859-1 8-bit characters using UTF-8 encoding:
>> s.encode('UTF-8')
=> "Çäëëî öîñëã!"
# Another way is just to repack the bytes into UTF-8:
>> s.bytes.pack('U*')
=> "Çäëëî öîñëã!"
Of course the proper way to do this, is not to let the numbers overflow into 8-bit space under any circumstance. Your encryption algorithm has a bug, and you need to ensure that the output is in the 7-bit ASCII range.
A better solution
Like @tadman suggested, you could use tr
instead:
AZ_SEQUENCE = *'A'..'Z' + *'a'..'z'
"Hello world!".tr(AZ_SEQUENCE.join, AZ_SEQUENCE.rotate(127).join)
=> "eBIIL tLOIA!
Rotating strings in Python
You can slice and add strings:
>>> s = 'HELLO'
>>> s[-1] + s[:-1]
'OHELL'
This gives you the last character:
>>> s[-1]
'O'
and this everything but the last:
>>> s[:-1]
'HELL'
Finally, add them with +
.
Related Topics
Problem Running Thinking Sphinx with Rails 2.3.5
Ruby Looks for Class Variable in the Object Instead of Specific Class
Linking Two Models in a Multi-Model Form
Markdown to Plain Text in Ruby
Rails S Return: [Bug] Segmentation Fault
Ruby Equivalent of C#'s 'Yield' Keyword, Or, Creating Sequences Without Preallocating Memory
How to Specify a Regex Character Range That Will Work in European Languages Other Than English
Is It Acceptable Having Parameter in Class Constructor
Nesting Too Deep' Error While Retrieving JSON Using Httparty
Exec the Cd Command in a Ruby Script
Why Is My Ruby Git Script Hook Run with the Wrong $Path
Having Difficulty Accessing Validation Errors in Sinatra
Parsing Large Xml with Nokogiri
When to Use Keyword Arguments Aka Named Parameters in Ruby
Fresh Install of Rails and Getting Openssl Errors: "Already Initialized Constant Openssl"
Why Does the Break Statement in Ruby Behave Differently When Using Proc.New V. the Ampersand Sign