How do I count the frequencies of the letters in user input? - ruby

How do I count the frequency of letters that appear in the word "supercaliforniamightly" when the user enters a word like that in Ruby, and print out stars or asterisks to count the number of letters that appear?
Here's my code:
puts "Enter string: "
text= gets.chomp
text.downcase!
words = text.split(//)
frequencies = Hash.new(0)
words.each{|item| frequencies[item] +=1}
frequencies = frequencies.sort_by{ |item, amount| amount}
frequencies.reverse!
frequencies.each do |item, amount|
puts item + " " + amount.to_s
end
The output I want is something like:
Enter a string:
uuuuiiii
u , 4 ****
i , 4 ****

I changed the output a little bit (removed the space before the comma) so that I don't look like uneducated.
puts "Enter string: "
gets.chomp.downcase
.each_char.with_object(Hash.new(0)){|c, h| h[c] += 1}
.sort_by{|_, v| v}
.reverse
.each{|k, v| puts k + ", " + v.to_s + " " + "*" * v}
Output:
Enter string:
uuuuiiii
i, 4 ****
u, 4 ****

Related

Reading word into an array [ruby]

Trying to create a ceaser cipher in Ruby.
The problem I'm facing is that when the program reaches the while loop, it only performs the desired operation on the very last letter of the inputted word. Before I delve further into what I've tried, please find the code:
#!/usr/bin/ruby
#print 65.chr ASCII code for A
#print 97.chr ASCII code for a
a = 0
b = 97
d = []
e = 0
# Just to print the alphabet alongside the ASCII value
# (For sanity checking)
while a <= 25
print b.chr + " "
print b.to_s + "\n"
a = a + 1
b = b + 1
end
puts "\n Please enter a word to translate"
word = gets.strip
# The desired effect is to move the letter along by key value
puts "Please enter a key"
k = gets.chomp.to_i
# In its current state, what happens is only the last char
# is moved along by the key value.
while e <= word.length
word.each_byte do |c|
d[e] = c + k
end
e = e + 1
end
puts d
I'm thinking that the problem lies with the logic for the while loop. The way I am going to attack this is by reading the pre-converted word into an array, as opposed to using the .each_byte object.
I don't know how to do that and the guides/questions I've found don't quite answer the question. If anyone knows how to do this, or knows a better way of solving this- I'd be much appreciative.
you don't need the last while loop
word.each_byte do |c|
d[e] = c + k
e = e + 1
end
Something a bit more verbose:
alphabet = ('a'..'z').to_a
new_word = ''
puts "\n Please enter a word to translate"
word = gets.strip
puts "Please enter a key"
k = gets.chomp.to_i
word.split('').each_with_index do |letter, index|
alphabet_index = alphabet.index(letter)
new_index = alphabet_index + key
new_word[index] = alphabet[new_index]
end
puts "Your translated word is #{new_word}"
Caesar cipher is a simple shift cipher
word.each_byte do |c|
p c + k
end
Managed to get it working, thanks for all the help... Code for anyone interested:
#!/usr/bin/ruby
#print 65.chr A
#print 97.chr a
a = 0
b = 65
y = 97
d = []
e = 0
while a <= 25
print y.chr + " = " + y.to_s + " "
print b.chr + " = " + b.to_s + " " + "\n"
a = a + 1
b = b + 1
y = y + 1
end
puts "\n Please enter a word to translate"
word = gets.strip
puts "Please enter a key"
k = gets.chomp.to_i
word.each_byte do |c|
d[e] = c + k
e = e + 1
end
print "\n"
a = 0
arlen = d.count
while a != arlen
print d[a].chr
a = a + 1
end
print k

How to get rid of a nested loop to make an arithmetic series more efficient?

I am pretty new to programming so bear with me, please.
So this is the code I have right now, I would like to know how to combine the two loops in the middle without changing the function of the program.
entry = " "
while entry != "q"
print "enter a number: "
num = gets.to_i
for x in 1..num
sum = 0
end
for y in 1..x
sum = sum + y
puts sum
end
print "press any key to continue (q to quit): "
entry = gets.chomp
end
Any help would be greatly appreciated!
Thank you!
Edit:
I guess I should clarify the function of this program; the user types in a number and then it calculates the value of each arithmetic series up to and including the number that the user put in.
So it if I type in 3 the result should display like this:
1
3
6
Sorry for the confusion!
Assuming that you want to calculate and puts a sum of numbers from 1 to the input number, I suggest following:
while entry != "q"
print "enter a number: "
num = gets.to_i
puts (1..num).sum
print "press any key to continue (q to quit): "
entry = gets.chomp
end
For edited question solution could be:
while entry != "q"
print "enter a number: "
num = gets.to_i
(1..num).inject(0) do |res, e|
res += e
p res
end
print "press any key to continue (q to quit): "
entry = gets.chomp
end

how to change nil into " " in array

#ask user for string input
puts "Enter Text"
# text user has entered
text = "Test text"
#put text into a array - "split" by just words and spaces
words = text.downcase.split('')
#array of letters a to z in array
a_z = ('a'..'z').to_a
#idealy - take each letter in text string and match them to a_z's index from 0-25
#space values in text are replaced in array with " " instead of nil
words.map { |x| a_z.index(x) }
I am trying to take text, split text, turn text into a number based on a to z index ... shift that number and recombine and turn back into text...
the problem I am encountering now is that spaces in my text array show up as nil since their is no numerical value for nil in the a-z index.
how can I replace nil with " " so that my array can for example look like this:
#=> [1,2," "]
instead of:
#=> [1,2,nil]
Code
def shift_and_remove(str, shift)
h = make_hash 'a'..'z', shift
h.default_proc = ->(h,k) {' '}
str.gsub(/./,h)
end
def make_hash(r, shift)
base = r.first.ord
r.each_with_object({}) { |c,h| h[c] = (base + (c.ord-base+shift) % 26).chr }
end
Examples
str = "Now is the time for 007 to appear!"
shift_and_remove(str, 0) #=> " ow is the time for to appear "
shift_and_remove(str, 1) #=> " px jt uif ujnf gps up bqqfbs "
shift_and_remove(str, 4) #=> " sa mw xli xmqi jsv xs ettiev "
shift_and_remove(str, -1) #=> " nv hr sgd shld enq sn zoodzq "
shift_and_remove(str, -2) #=> " mu gq rfc rgkc dmp rm ynncyp "
Notice that in all cases "N" has been converted to a space, "w" of "Now" has been shifted to "a" in the third example and "a" of "appear" has been shifted to "z" and "y", respectively, in the last two examples.
Explanation
When shift #=> 2, the hash is as follows:
h[c] = ('a'.ord + (c.ord-'a'.ord+shift) % 26).chr }
#=> {"a"=>"c", "b"=>"d", "c"=>"e", "d"=>"f", "e"=>"g", "f"=>"h", "g"=>"i",
# "h"=>"j", "i"=>"k", "j"=>"l", "k"=>"m", "l"=>"n", "m"=>"o", "n"=>"p",
# "o"=>"q", "p"=>"r", "q"=>"s", "r"=>"t", "s"=>"u", "t"=>"v", "u"=>"w",
# "v"=>"x", "w"=>"y", "x"=>"z", "y"=>"a", "z"=>"b"}
All letters "a" to "x" are mapped to the next letter, whereas "y" and "z" are mapped to "a" and "b" respectively. Similarly, when shift #=> -1, "a" is mapped to "z" and all other letters are mapped to the preceding letter.
This uses the form of String#gsub that employs a hash to determine the replacement of each character in the string (/./). The method Hash#default_proc= creates a proc that gives the value of h[k] when the hash h does not have a key k, namely a space.
Capital letters
If any capital letters present are to be shifted to capital letters (rather than converted to spaces), only a slight modification is required.
def shift_and_remove(str, shift)
h = make_hash('a'..'z', shift).merge make_hash('A'..'Z', shift)
h.default_proc = ->(h,k) {' '}
str.gsub(/./,h)
end
shift_and_remove(str, 0) #=> "Now is the time for to appear "
shift_and_remove(str, 1) #=> "Opx jt uif ujnf gps up bqqfbs "
shift_and_remove(str, 4) #=> "Rsa mw xli xmqi jsv xs ettiev "
shift_and_remove(str, -1) #=> "Mnv hr sgd shld enq sn zoodzq "
shift_and_remove(str, -2) #=> "Lmu gq rfc rgkc dmp rm ynncyp "

customised random string generator in ruby

Below is my code to generate a random string by taking the number of length from the user . I want to enhance this by asking the user to enter number of numbers and special characters to be included and generate a string to match those requirements.
For example:
length: 7
number of numbers: 2
number of special chars: 1
Then my output should be something like:
ab2hg3!
Below is my code:
puts "enter the minimum length of the password"
lengths = gets.to_i
puts "enter the number of numbers to be included"
num = gets.to_i
def gen_pass(lengths)
chars = ('a'..'z').to_a + ('A'..'Z').to_a + (0..10).to_a
Array.new(lengths) {chars.sample}.join
end
puts gen_pass(lengths)
puts gen_pass(lengths)
Try this:
def gen_pass(lengths, number_of_numbers, number_of_special_chars)
chars = ('a'..'z').to_a + ('A'..'Z').to_a + (0..10).to_a
digits = (0..9).to_a
special_chars = "?<>',?[]}{=-)(*&^%$#`~{}".split('')
remaining_letters_count = lengths - number_of_numbers - number_of_special_chars
n = digits.sample(number_of_numbers)
s = special_chars.sample(number_of_special_chars)
c = chars.sample(remaining_letters_count)
str = n << s << c
str.flatten.shuffle.join
end
puts gen_pass(lengths, number_of_numbers, number_of_special_chars)
If this is an exercise you should do it yourself. If it's not, you are certainly doing it wrong.
Anyway, here is my try :
#!/usr/bin/env ruby
def secret(l,n,s)
random_string =
[*('A'..'Z'),*('a'..'z')].sample(l-n-s) +
[*('0'..'9')].sample(n) +
[*('!'..'/')].sample(s)
random_string.shuffle.join
end
p secret(5,2,1)
=> ".4F0r"

Public key encryption demo in Ruby

I wrote up the ruby script below to help my students understand public key encryption. I followed the "pencil and paper" method shown here: http://sergematovic.tripod.com/rsa1.html
This works fine as long as 29 isn't chosen as either p or q. If 29 is picked, it hangs on calculating the secret key. Can anyone tell me why that is?
#!/usr/bin/env ruby -wKU
#initialize
primes, p, q, n, z, k, j, m,e,d = nil
def prime
primes = [2,3,5,7,11,13,17,19,23,29,31]
primes.sample
end
#pick p
p= prime
puts "p: " + p.to_s
#pick q
q=p
while p==q
q = prime
end
puts "q: " + q.to_s
#find n
n=p*q
puts "n: " + n.to_s
#find z
z=(p-1)*(q-1)
puts "z: " + z.to_s
#pick a relative prime of the totient
k=7
puts "k: " + k.to_s
#calculate secret key
j=0
while j*k % z != 1
j+=1
end
puts "j: " + j.to_s
#message
m=16
puts "Message: " + m.to_s
#encrypt
e = m**k % n
puts "Encrypted: " + e.to_s
#decrypt
d = e**j % n
puts "Decrypted: " + d.to_s
When 29 is chosen as p or q, z has 28 as a factor, and thus k = 7 is not a relative prime of the totient as your comment claims!
(This means j*k % z is always a multiple of 7, so your loop never terminates.)

Resources