Replacing every letter in a given string with the letter following in the alphabet [closed] - ruby

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I have to replace every letter in a string with the letter following it in the alphabet (i.e. c becomes d, z becomes a), capitalize every vowel (a, e, i, o, u), and return the modified string. I'm trying to find solutions without calling any functions like sort or find.
I have this:
def LetterChanges(str)
Changed_Letter = ""
alphabet = [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z]
for i in 0..str.length do
if str[i] ==
str[i] = alphabet[i] + 1
return str
end
but I am lost. Any help is appreciated.

You are being asked to "map" each letter of the alphabet to another letter, so you will want to use the method Enumerable#map.
VOWELS = "aeiou"
letters = ('a'..'z').to_a
#=> ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
letters.map do |c|
<code referencing c>
end
#=> ['b', 'c', 'd', 'E', 'f',..., 'z', 'A]
Now let's fill in the code, using the methods:
String#succ, which, given a character, returns the character with the next-higher ASCII value. For example, "b".ord #=> 98, so "b".succ #=> "c", since "c".ord #=> 99. Since "z".succ #=> 'aa', we need to treat "z" as a special case. String#succ is the same as String#next.
String#include?, which, given a string, returns true or false depending on whether include?'s argument (a string) is included in the receiver. For example, "cat".include?("at") #=> true; "cat".include?("a") #=> true; "cat".include?("z") #=> false. Note that VOWELS, since it begins with a capital letter, is a constant.
String#upcase, which converts all lowercase letters in a given string to upper case (and leaves all other characters unchanged).
letters.map do |c|
if c == 'z'
'A'
else
s = c.succ
if VOWELS.include?(s)
s.upcase
else
s
end
end
end
#=> ["b", "c", "d", "E", "f", "g", "h", "I", "j", "k", "l", "m", "n",
# "O", "p", "q", "r", "s", "t", "U", "v", "w", "x", "y", "z", "A"]
You could instead write this using a case statement and Ruby's ternary operator:
letters.map do |c|
case c
when 'z'
'A'
else
s = c.succ
VOWELS.include?(s) ? s.upcase : s
end
end
or you could make use of the methods String#ord and Integer#chr:
letters.map do |c|
s = ('a'.ord + ((c.ord-'a'.ord+1) % 26)).chr
VOWELS.include?(s) ? s.upcase : s
end
end
If, for example, c = 'r'
('a'.ord + ((c.ord-'a'.ord+1) % 26).chr
#=> (97 + ((114-97+1) % 26).chr
#=> (97 + 18 % 26).chr
#=> (97 + 18).chr
#=> 115.chr
#=> 's'
If, however, c = 'z'
('a'.ord + ((c.ord-'a'.ord+1) % 26).chr
#=> (97 + ((122-97+1) % 26).chr
#=> (97 + 26 % 26).chr
#=> (97 + 0).chr
#=> 97.chr
#=> 'a'
One more way. (You can figure out why this works.)
letters.map do |c|
s = c.succ[0]
VOWELS.include?(s) ? s.upcase : s
end
You might instead wish to create a hash.
letter_mapping = {}
letters.each do |c|
s = c.succ[0]
letter_mapping[c] = VOWELS.include?(s) ? s.upcase : s
end
letter_mapping
#=> { "a"=>"b", "b"=>"c", "c"=>"d", "d"=>"E", "e"=>"f", "f"=>"g", "g"=>"h",
# "h"=>"I", "i"=>"j", "j"=>"k", "k"=>"l", "l"=>"m", "m"=>"n", "n"=>"O",
# "o"=>"p", "p"=>"q", "q"=>"r", "r"=>"s", "s"=>"t", "t"=>"U", "u"=>"v",
# "v"=>"w", "w"=>"x", "x"=>"y", "y"=>"z", "z"=>"A"}
so, for example, letter_mapping['r'] #=> "s".
In time you will find that the Ruby way of writing this is:
letters.each_with_object({}) do |c, letter_mapping|
s = c.succ[0]
letter_mapping[c] = VOWELS.include?(s) ? s.upcase : s
end
#=> { "a"=>"b", ... "z"=>"A"}
One last thing. Enumerable#map is an instance method for every class that includes the Enumerable module. One such class is Array:
Array.included_modules
#=> [Enumerable, Kernel]
Array.instance_methods.include?(:map)
#=> true
Array has use of all of the module Enumerable's methods, just as though they had been defined in Array. That's why map works when the receiver is an array.
Another class that includes Enumerable is Range:
Range.included_modules
#=> [Enumerable, Kernel]
Range.instance_methods.include?(:map)
#=> true
Therefore, instead of writing:
letters = ('a'..'z').to_a
we could (should) write:
letters = ('a'..'z')
and all the above code would work just fine.

You can try this, it will replace a letter with its following letter also it will capitalize vowels.
def letter_changes(str)
alphabets = ('a'..'z').to_a
vowels = ["a","e","i","o","u"]
for i in 0..(str.length-1) do
index = (alphabets.index(str[i]) == (alphabets.size - 1) ? 0 : (alphabets.index(str[i]) + 1))
str[i] = alphabets[index]
str[i] = str[i].upcase if vowels.include?(str[i])
end
puts str
end
## call function
letter_changes("cadcarz")
## OUTPUT
dbEdbsA

Related

Caesar Cipher - Ruby

I have to make a function that receives a phrase and encrypts it. The cipher is to each letter in alphabet the encrypted letter is 3 letter ahead.
Example
Alphabet: A B C D E F G ... X Y Z
Ciphered: D E F G H I J ... A B C
If this is my alphabet in Ruby:
a = ['a','b','c','d','e']
I need to map it to:
a = ['c','d','e','a','b']
I've tried iterate twice the array and remove some indexes but I know I'm missing something.
UPDATE--------------------------------------------------------------------
I've managed to solve the six tests where I receive a phrase and have to encrypts as the test require.
Received phrase: prefiro perder a guerra e ganhar a paz
Phrase expected: suhilur#shughu#d#jxhuud#h#jdqkdu#d#sd}
I realize that to cypher the phrase I should change the letters positions 3 positions ahead in the ascii table.
Example: The letter 'a' should be encrypted as 'd', The letter 'z' should be encrypted as '}' and also the 'space' 3 positions ahead in the ascii table is '#'.
Here follows the code I used to solve this:
def cipher(text)
key = 3
cipher_text = text.chars.map {|x| x.ord}
.map {|x| x+key}
cipher_text.map { |x| x.chr }.join
end
def decipher(text)
key = 3
decipher_text = text.chars.map {|x| x.ord}
.map {|x| x-key}
decipher_text.map { |x| x.chr }.join
end
For encryption mentioned in the comments use String.tr method
I have to make a function that receives a phrase and encrypts it. The
cipher is to each letter in alphabet the encrypted letter is 3 letter
ahead.
phrase = "abcd st xyz"
encrypted = phrase.tr("A-Za-z ", "D-ZA-Cd-za-c#")
# => "defg#vw#abc"
Update
Please notice that the letter 'z' (at the end of the phrase) means
'}'
You can map xyz character to {|} explicitly
phrase = "prefiro perder a guerra e ganhar a paz"
encrypted = phrase.tr("A-Wa-wXYZxyz ", "D-WA-Cd-wa-c{|}{|}#")
# => "suhilur#shughu#d#jxhuud#h#jdqkdu#d#sd}"
Not sure I understand your question, but the data looks like you rotate the elements in the array. In Ruby you have a special method for that.
a = %w[a b c d] #=> ["a", "b", "c", "d"]
a.rotate #=> ["b", "c", "d", "a"]
a #=> ["a", "b", "c", "d"]
a.rotate(2) #=> ["c", "d", "a", "b"]
a.rotate(-3) #=> ["b", "c", "d", "a"]
Given an alphabet:
alphabet = ('A'..'Z').to_a
#=> ["A", "B", "C", "D", "E", ..., "V", "W", "X", "Y", "Z"]
You can create the ciphered one by calling rotate:
ciphered = alphabet.rotate(3)
#=> ["D", "E", "F", "G", "H", ..., "Y", "Z", "A", "B", "C"]
And create a mapping from one to the other:
to_cipher = alphabet.zip(ciphered).to_h
#=> {"A"=>"D", "B"=>"E", "C"=>"F", ..., "X"=>"A", "Y"=>"B", "Z"=>"C"}
Now, to encrypt a given string, we have to run each character through that hash:
'HELLO WORLD!'.each_char.map { |char| to_cipher[char] }.join
#=> "KHOORZRUOG"
Well, almost. That also removed the space and exclamation mark. We can fix this by providing a fallback for characters that don't occur in the hash:
'HELLO WORLD!'.each_char.map { |char| to_cipher.fetch(char, char) }.join
#=> "KHOOR ZRUOG!"
Or, with regular expressions using gsub:
'HELLO WORLD!'.gsub(Regexp.union(to_cipher.keys), to_cipher)
#=> "KHOOR ZRUOG!"

Print elements of array of arrays of different size in same line in Ruby

Maybe someone could help me with this. I have an array of arrays. The internal arrays have different sizes (from 2 to 4 elements).
letters = [["A", "B"],["C", "D", "F", "G"],["H", "I", "J" ]]
I'm trying to print in a same line each array havins as first column element[0] and element[1] joined, as 2nd column element[0], element[1], element[2] joined as 3rd column element[0], element[1], element[3] joined. Elements 2 and 3 not always exist.
The output I'm trying to get is like this:
AB
CD CDF CDG
HI HIJ
I'm doing in this way but I'm getting this error.
letters.map{|x| puts x[0]+x[1] + "," + x[0]+x[1]+x[2] + "," + x[0]+x[1]+x[3]}
TypeError: no implicit conversion of nil into String
from (irb):1915:in "+"
from (irb):1915:in "block in irb_binding"
from (irb):1915:in "map"
from (irb):1915
from /usr/bin/irb:11:in "<main>"
letters.each do |a,b,*rest|
puts rest.each_with_object([a+b]) { |s,arr| arr << arr.first + s }.join(' ')
end
prints
AB
CD CDF CDG
HI HIJ
The steps are as follows.
Suppose
letters = [["C", "D", "F", "G"],["H", "I", "J" ]]
Then
enum0 = letters.each
#=> #<Enumerator: [["C", "D", "F", "G"], ["H", "I", "J"]]:each>
The first element of this enumerator is generated and passed to the block, and the three block variables are assigned values.
a, b, *rest = enum0.next
#=> ["C", "D", "F", "G"]
a
#=> "C"
b
#=> "D"
rest
#=> ["F", "G"]
Next, we obtain
enum1 = rest.each_with_object([a+b])
#=> rest.each_with_object(["CD"])
#=> #<Enumerator: ["F", "G"]:each_with_object(["CD"])>
The first element of this enumerator is generated and passed to the block, and the block variables are assigned values.
s, arr = enum1.next
#=> ["F", ["CD"]]
s
#=> "F"
arr
#=> ["CD"]
The block calculation is now performed.
arr << arr.first + s
#=> arr << "CD" + "F"
#=> ["CD", "CDF"]
The second and last element of enum1 is generated and passed to the block, and block variables are assigned values and the block is computed.
s, arr = enum1.next
#=> ["G", ["CD", "CDF"]]
arr << arr.first + s
#=> ["CD", "CDF", "CDG"]
When an attempt to generate another element from enum1 we obtain
enum1.next
#StopIteration: iteration reached an end
Ruby handles the exception by breaking out of the block and returning arr. The elements of arr are then joined:
arr.join(' ')
#=> "CD CDF CDG"
and printed.
The second and last element of enum0 is now generated, passed to the block, and the three block variables are assigned values.
a, b, *rest = enum0.next
#=> ["H", "I", "J"]
a
#=> "H"
b
#=> "I"
rest
#=> ["J"]
The remaining calculations are similar.
Some readers may be unfamiliar with the method Enumerable#each_with_object, which is widely used. Read the doc, but note that here it yields the same result as the code written as follows.
letters.each do |a,b,*rest|
arr = [a+b]
rest.each { |s| arr << arr.first + s }
puts arr.join(' ')
end
By using each_with_object we avoid the need for the statement arr = [a+b] and the statement puts arr.join(' '). The functions of those two statements are of course there in the line using each_with_object, but most Ruby users prefer the flow when when chaining each_with_object to join(' '). One other difference is that the value of arr is confined to each_with_object's block, which is good programming practice.
Looks like you want to join the first two letters, then take the cartesian product with the remaining.
letters.each do |arr|
first = arr.take(2).join
rest = arr.drop(2)
puts [first, [first].product(rest).map(&:join)].join(" ")
end
This provides the exact output you specified.
Just out of curiosity, Enumerable#map-only solution.
letters = [["A", "B"],["C", "D", "F", "G"],["H", "I", "J" ]]
letters.map do |f, s, *rest|
rest.unshift(nil).map { |l| [f, s, l].join }.join(' ')
end.each(&method(:puts))
#⇒ AB
# CD CDF CDG
# HI HIJ

Ruby: how to count the frequencies of the letters

HELP: do a frequency method: calculate the frequency each letter appears in “str” and assign the output to “letters”. The method should count lower and uppercase as the same letter. but I have the error about NoMethodError: undefined method
Thank you
here is my code
class MyString
attr_accessor :str
attr_reader :letters
def initialize
#str = "Hello World!"
#letters = Hash.new()
end
def frequency
#letters = #str.split(" ").reduce(#letters) { |h, c| h[c] += 1; h}
end
def histogram
##letters.each{|key,value| puts "#{key} + ':' + value.to_s + '*' * #{value}" }
end
end
The error shows :
irb(main):009:0> txt1.frequency
NoMethodError: undefined method `+' for nil:NilClass
from assignment.rb:11:in `block in frequency'
from assignment.rb:11:in `each'
from assignment.rb:11:in `reduce'
from assignment.rb:11:in `frequency'
from (irb):9
from /usr/bin/irb:12:in `<main>'
When you try to add 1 to a value in the hash that doesn't exist, it tries to add 1 to nil, which isn't allowed. You can change the hash so that the default value 0, not nil.
#letters = Hash.new(0)
Now, your program right now is counting word frequencies, not letter frequencies (split(" ") splits on spaces, not on each character). To split on each character, use the appropriately named each_char method.
#letters = #str.each_char.reduce(#letters) { |h, c| h[c] += 1; h}
Whenever one uses a counting hash (#Silvio's answer) one can instead use Enumerable#group_by, which is what I've done here.
str = "It was the best of times, it was the worst of times"
str.gsub(/[[:punct:]\s]/, '').
downcase.
each_char.
group_by(&:itself).
each_with_object({}) { |(k,v),h| h[k] = v.size }
#=> {"i"=>4, "t"=>8, "w"=>3, "a"=>2, "s"=>6, "h"=>2, "e"=>5,
# "b"=>1, "o"=>3, "f"=>2, "m"=>2, "r"=>1}
The steps are as follows.
a = str.gsub(/[[:punct:]\s]/, '')
#=> "Itwasthebestoftimesitwastheworstoftimes"
b = a.downcase
#=> "itwasthebestoftimesitwastheworstoftimes"
e = b.each_char
#=> #<Enumerator: "itwasthebestoftimesitwastheworstoftimes":each_char>
f = e.group_by(&:itself)
#=> {"i"=>["i", "i", "i", "i"],
# "t"=>["t", "t", "t", "t", "t", "t", "t", "t"],
# ...
# "r"=>["r"]}
f.each_with_object({}) { |(k,v),h| h[k] = v.size }
#=> < return value shown above >
Let's look more closely at the last step. The first key-value pair of the hash f is passed to the block as a two-element array, together with the initial value of the hash h:
(k,v), h = [["i", ["i", "i", "i", "i"]], {}]
#=> [["i", ["i", "i", "i", "i"]], {}]
Applying the rules of disambiguation (or decomposition), we obtain the following.
k #=> "i"
v #=> ["i", "i", "i", "i"]
h #=> {}
The block calculation is performed:
h[k] = v.size
#=> h["i"] = 4
So now
h => { "i"=>4 }
The next key-value pair is passed to the block, along with the current value of h:
(k,v), h = [["t", ["t", "t", "t", "t", "t", "t", "t", "t"]], { "i"=>4 }]
#=> [["t", ["t", "t", "t", "t", "t", "t", "t", "t"]], {"i"=>4}]
k #=> "t"
v #=> ["t", "t", "t", "t", "t", "t", "t", "t"]
h #=> {"i"=>4}
h[k] = v.size
#=> 8
So now
h #=> {"i"=>4, "t"=>8}
The remaining calculations are similar.
The method Enumerable#tally, which made its debut in Ruby v2.7, is purpose-built for this task:
str.gsub(/[[:punct:]\s]/, '').downcase.each_char.tally
#=> {"i"=>4, "t"=>8, "w"=>3, "a"=>2, "s"=>6, "h"=>2,
# "e"=>5, "b"=>1, "o"=>3, "f"=>2, "m"=>2, "r"=>1}

How to make the array of the alphabet to rotate from "z" to "a" (ruby)

I am doing a Caesar cipher. I thought that the unless statement will work but it doesn't with or without then. Then I changed the unless with if and put ; in the place of then and it reads : undefined method `>' for nil:NilClass.
def caesar_cipher(input, key)
input.each do |x|
numbers = x.ord + key.to_i unless (numbers > 122) then numbers = x.ord + key - 26
letters = numbers.chr
print letters
end
end
puts "Write the words you want to be ciphered: "
input = gets.chomp.split(//)
puts "Write the key (1 - 26): "
key = gets.chomp
caesar_cipher(input,key)
Here are a couple of Ruby-like ways to write that:
#1
def caesar_cipher(input, key)
letters = ('a'..'z').to_a
input.each_char.map { |c| letters.include?(c) ?
letters[(letters.index(c)+key) % 26] : c }.join
end
caesar_cipher("this is your brown dog", 2)
#=> "vjku ku aqwt dtqyp fqi"
#2
def caesar_cipher(input, key)
letters = ('a'..'z').to_a
h = letters.zip(letters.rotate(key)).to_h
h.default_proc = ->(_,k) { k }
input.gsub(/./,h)
end
caesar_cipher("this is your brown dog", 2)
#=> "vjku ku aqwt dtqyp fqi"
The hash h constructed in #2 equals:
h = letters.zip(letters.rotate(key)).to_h
#=> {"a"=>"c", "b"=>"d", "c"=>"e", "d"=>"f", "e"=>"g", "f"=>"h",
# ...
# "u"=>"w", "v"=>"x", "w"=>"y", "x"=>"z", "y"=>"a", "z"=>"b"}
h.default_proc = ->(_,k) { k } causes
h[c] #=> c
if c is not a lowercase letter (e.g., a space, capital letter, number, punctuation, etc.)
If you write a branch with condition (if or unless) at the end of a line, after an initial statement, there are two things that apply and affect you:
The condition is assessed before the statement on its left. In your case that means numbers has not been assigned yet so it is nil.
The branch decision is whether or not to run the initial statement, you do not branch to the statement after the then.
You can solve this simply by converting your condition to an if and moving it to a separate line:
def caesar_cipher(input, key)
input.each do |x|
numbers = x.ord + key.to_i
if (numbers > 122)
numbers = x.ord + key - 26
end
letters = numbers.chr
print letters
end
end
There are arguably better ways of coding this cipher in Ruby, but this should solve your immediate problem.
There is a more elegant way to loop repeating sequences in ruby. Meet Enumerable#cycle.
('a'..'z').cycle.take(50)
# => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
# "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
# "u", "v", "w", "x", "y", "z", "a", "b", "c", "d",
# "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
# "o", "p", "q", "r", "s", "t", "u", "v", "w", "x"]
Therefore, translating a single letter given a key can be written as:
('a'..'z').cycle.take(letter.ord + key.to_i - 'a'.ord.pred).last
And the entire method can look prettier:
def caesar_cipher(phrase, key)
phrase.each_char.map do |letter|
('a'..'z').cycle.take(letter.ord + key.to_i - 'a'.ord.pred).last
end.join
end
puts caesar_cipher('abcxyz', 3) # => defabc
Note that this is slower than the alternative, but it also has the benefit that it's easier to read and the key can be any number.

In cryptography, a Caesar cipher Ruby

In cryptography, there is a Caesar cipher. I am trying to build one in Ruby, but I don't know how to use capital letters in my range ('a'..'z').to_a.join. How do I use capital letters?
class Caesar
def initialize(shift)
alphabet = ('a'..'z').to_a.join
i = shift % alphabet.size
#decrypt = alphabet
#encrypt = alphabet[i..-1] + alphabet[0...i]
end
def encrypt(string)
string.tr(#decrypt, #encrypt)
end
def decrypt(string)
string.tr(#encrypt, #decrypt)
end
end
cipher_1 = Caesar.new(1)
s = 'A man, a plan, a canal: Panama!'
puts s
s_encoded = cipher_1.encrypt(s)
puts s_encoded
pudaats = cipher_1.decrypt(s_encoded)
puts pudaats
OUTput
A man, a plan, a canal: Panama!
A nbo, b qmbo, b dbobm: Pbobnb!
A man, a plan, a canal: Panama!
But i need Out Put
A man, a plan, a canal: Panama!
B nbo, b qmbo, b dbobm: Qbobnb!
A man, a plan, a canal: Panama!
The issue here lies within what you've defined as the alphabet.
a..z # ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
Notice here that there's no capital letters. The only letters that are being transposed are the lowercase ones. Therefore you will need to change your range to include uppercase characters as well:
alphabet = (("A".."Z").to_a + ("a".."z").to_a).join
Then you will get the correct result.
LOWER_CASE = ('a'.ord .. 'z'.ord)
UPPER_CASE = ('A'.ord .. 'Z'.ord)
def shift(c, by)
byte = c.ord
if LOWER_CASE.include?(byte) then
((((byte - LOWER_CASE.min) + by) % 26) + LOWER_CASE.min).chr
elsif UPPER_CASE.include?(byte) then
((((byte - UPPER_CASE.min) + by) % 26) + UPPER_CASE.min).chr
else
c
end

Resources