how to change nil into " " in array - ruby

#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 "

Related

Codewars: "Return or rotate": why isn't my attempted solution working?

These were the instructions given on Codewars (https://www.codewars.com/kata/56b5afb4ed1f6d5fb0000991/train/ruby):
The input is a string str of digits. Cut the string into chunks (a chunk here is a substring of the initial string) of size sz (ignore the last chunk if its size is less than sz).
If a chunk represents an integer such as the sum of the cubes of its digits is divisible by 2, reverse that chunk; otherwise rotate it to the left by one position. Put together these modified chunks and return the result as a string.
If
sz is <= 0 or if str is empty return ""
sz is greater (>) than the length of str it is impossible to take a chunk of size sz hence return "".
Examples:
revrot("123456987654", 6) --> "234561876549"
revrot("123456987653", 6) --> "234561356789"
revrot("66443875", 4) --> "44668753"
revrot("66443875", 8) --> "64438756"
revrot("664438769", 8) --> "67834466"
revrot("123456779", 8) --> "23456771"
revrot("", 8) --> ""
revrot("123456779", 0) --> ""
revrot("563000655734469485", 4) --> "0365065073456944"
This was my code (in Ruby):
def revrot(str, sz)
# your code
if sz > str.length || str.empty? || sz <= 0
""
else
arr = []
while str.length >= sz
arr << str.slice!(0,sz)
end
arr.map! do |chunk|
if chunk.to_i.digits.reduce(0) {|s, n| s + n**3} % 2 == 0
chunk.reverse
else
chunk.chars.rotate.join
end
end
arr.join
end
end
It passed 13/14 test and the error I got back was as follows:
STDERR/runner/frameworks/ruby/cw-2.rb:38:in `expect': Expected: "", instead got: "095131824330999134303813797692546166281332005837243199648332767146500044" (Test::Error)
from /runner/frameworks/ruby/cw-2.rb:115:in `assert_equals'
from main.rb:26:in `testing'
from main.rb:84:in `random_tests'
from main.rb:89:in `<main>'
I'm not sure what I did wrong, I have been trying to find what it could be for over an hour. Could you help me?
I will let someone else identify the problem with you code. I merely wish to show how a solution can be speeded up. (I will not include code to deal with edge cases, such as the string being empty.)
You can make use of two observations:
the cube of an integer is odd if and only if the integer is odd; and
the sum of collection of integers is odd if and only if the number of odd integers is odd.
We therefore can write
def sum_of_cube_odd?(str)
str.each_char.count { |c| c.to_i.odd? }.odd?
end
Consider groups of 4 digits in the last example, "563000655734469485".
sum_of_cube_odd? "5630" #=> false (so reverse -> "0365")
sum_of_cube_odd? "0065" #=> true (so rotate -> "0650")
sum_of_cube_odd? "5734" #=> true (so rotate -> "7345")
sum_of_cube_odd? "4694" #=> true (so rotate -> "6944")
so we are to return "0365065073456944".
Let's create another helper.
def rotate_chars_left(str)
str[1..-1] << s[0]
end
rotate_chars_left "0065" #=> "0650"
rotate_chars_left "5734" #=> "7345"
rotate_chars_left "4694" #=> "6944"
We can now write the main method.
def revrot(str, sz)
str.gsub(/.{,#{sz}}/) do |s|
if s.size < sz
''
elsif sum_of_cube_odd?(s)
rotate_chars_left(s)
else
s.reverse
end
end
end
revrot("123456987654", 6) #=> "234561876549"
revrot("123456987653", 6) #=> "234561356789"
revrot("66443875", 4) #=> "44668753"
revrot("66443875", 8) #=> "64438756"
revrot("664438769", 8) #=> "67834466"
revrot("123456779", 8) #=> "23456771"
revrot("563000655734469485", 4) #=> "0365065073456944"
It might be slightly faster to write
require 'set'
ODD_DIGITS = ['1', '3', '5', '7', '9'].to_set
#=> #<Set: {"1", "3", "5", "7", "9"}>
def sum_of_cube_odd?(str)
str.each_char.count { |c| ODD_DIGITS.include?(c) }.odd?
end

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 merge the hours minutes and seconds from a multiline string in ruby?

i am getting a multiline string in ruby. lets say
s = "00:01:07,11-234-090
00:05:01,22-080-080
00:05:00,11-234-090"
this string is separated by new line characters (\n). i want a method which sums up the duration (left side part of comma in every new line ex (00:01:07) based on the string(right side part after the comma in every line) means the first line and last line contains the same string after the comma (11-234-090). and in the second line the string after the comma is (22-080-080) as there is no same string in the entire string i want to keep this line as same.
input = "00:01:07,11-234-090
00:05:01,22-080-080
00:05:00,11-234-090"
output : "00:06:07,11-234-090
00:05:01,22-080-080"
here the 00 represent hour , 06 represents minutes and 07 represents seconds.
i want a method which returns the desired string by taking the input string
def returs_sumation_string(input_string)
return desired_string
end
i tried this:
s = "00:01:07,400-234-090
00:05:01,701-080-080
00:05:00,400-234-090"
splited = s.split("\n")
splited.map!{|s| s.strip}
this much i know. do not know how to proceed further.
Code
def aggregate(str)
str.strip.split(/\n\s*/).group_by { |s| s[-10..-1] }.map do |k,a|
secs = a.reduce(0) do |t,s|
h,m,s = s.split(":").map(&:to_i)
t + 3600*h + 60*m + s
end
h,secs = secs.divmod(3600)
m,secs = secs.divmod(60)
"%0#{h>99 ? 3:2 }d:%02d:%02d,%s" % [h,m,secs,k]
end
end
Example
str = "00:01:07,11-234-090
00:05:01,22-080-080
00:05:00,11-234-090"
aggregate(str)
#=> ["00:06:07,11-234-090", "00:05:01,22-080-080"]
Explanation
See the docs for methods Enumerable#group_by, Enumerable#reduce (aka inject) and Fixnum#divmod.
For str given in the example, the main steps are as follows.
b = str.strip
#=> "00:01:07,11-234-090\n 00:05:01,22-080-080\n 00:05:00,11-234-090"
c = b.split(/\n\s*/)
#=> ["00:01:07,11-234-090", "00:05:01,22-080-080", "00:05:00,11-234-090"]
d = c.group_by { |s| s[-10..-1] }
#=> {"11-234-090"=>["00:01:07,11-234-090", "00:05:00,11-234-090"],
# "22-080-080"=>["00:05:01,22-080-080"]}
d.map do |k,a|
secs = a.reduce(0) do |t,s|
h,m,s = s.split(":").map(&:to_i)
t + 3600*h + 60*m + s
end
h,secs = secs.divmod(3600)
m,secs = secs.divmod(60)
"%0#{h>99 ? 3:2 }d:%02d:%02d,%s" % [h,m,secs,k]
end
#=> ["00:06:07,11-234-090", "00:05:01,22-080-080"]
Now let's break down the last step. map passes the first element of c (an array containing a key-value pair) to the block, to which the block variables are assigned, using parallel assignment (sometimes called multiple assignment):
k, a = d.first
#=> ["11-234-090", ["00:01:07,11-234-090", "00:05:00,11-234-090"]]
k #=> "11-234-090"
a #=> ["00:01:07,11-234-090", "00:05:00,11-234-090"]
and the block calculation is performed. First we calculate the total number of seconds for the two elements of a. I've added some puts statements to show the calculations.
secs = a.reduce(0) do |t,s|
puts " t=#{t}, s=#{s}"
puts " h,m,s = #{s.split(":")}"
h,m,s = s.split(":").map(&:to_i)
puts " h=#{h}, m=#{m}, s=#{s}"
puts " t + 3600*h + 60*m + s=#{t + 3600*h + 60*m + s}"
t + 3600*h + 60*m + s
end
# t=0, s=00:01:07,11-234-090
# h,m,s = ["00", "01", "07,11-234-090"]
# h=0, m=1, s=7
# t + 3600*h + 60*m + s=67
# t=67, s=00:05:00,11-234-090
# h,m,s = ["00", "05", "00,11-234-090"]
# h=0, m=5, s=0
# t + 3600*h + 60*m + s=367
#=> 367
Continuing,
h,secs = secs.divmod(3600)
#=> [0, 367]
h
#=> 0
secs
#=> 367
m,secs = secs.divmod(60)
#=> [6, 7]
m
#=> 6
secs
#=> 7
"%0#{h>99 ? 3:2 }d:%02d:%02d,%s" % [h,m,secs,k]
#=> "00:06:07,11-234-090"
The final statement, the formatting of the string that is returned, uses format codes listed in the doc for Kernel::format. "%02d" means to format an integer 2 characters wide with zeroes used for any left padding required (e.g., "%02d" % [9] #=> "09"). "%0#{h>99 ? 3:2 }" means that the field width for hours should be 3 if there are more than 99 hours, else the width is 2.
Calculations for the second element of c are similar.
The group_by expression, group_by { |s| s[-10..-1] } means group the strings (produced by split) by the last 10 characters of the string. Alternatively, one could replace the first line with the following.
str.strip.split(/\n\s*/).group_by { |s| s.split(",").last }

ruby access non-vowels of a string in condition line without arrays

I need to turn a string that contains (among other letters) a sequence of 3 letters consisting of a non-vowel, an "o" and the same non-vowel again
into
a string that contains (other letters and) only that non-vowel.
like
"kok" #=> "k"
"mom" #=> "m"
"lol" #=> "l"
"kokmomloljk" #=> "kmljk"
I would like my code to be as compact as possible and only use string methods.
str.each_char { | i | if i == /[^aeiou]/ and i == str[i.index + 2] and str[i.index + 1] == "o"
str = str.delete(str.slice(str[i.index + 1], 2))
end
}
The output is the unchanged string. Thank you in advance.
R = /
([^aeiou]) # match a consonant in capture group 1
o # match an 'o'
\1 # match the contents of capture group 1
/x # free-spacing regex definition mode
def my_method(str)
str.gsub(R,'\1')
end
my_method "my dog kok has fleas"
#=> "my dog k has fleas"
my_method "much momentum"
#=> "much mentum"
my_method "'plolly' is not a word"
#=> "'plly' is not a word"
my_method "abkokcmomloljkde"
#=> "abkcmljkde"
my_method "bub"
#=> "bub"
I was wondering if you could do this in a more functional non-destructieve way with map and, yes you can, but not more compact than the other answers:
str = "iskakmomlol"
VOWEL = /[aeiou]/
RESONANT = /[^aeiou]/
str.chars.map.with_index { |c, i|
prevb, prev, nxt, scnd = str[i - 2], str[i - 1], str[i + 1], str[i + 2]
if i > str.length - 1 || i == 0 then c
elsif c =~ RESONANT && nxt =~ VOWEL && c == scnd then c
elsif c =~ VOWEL && prev =~ RESONANT && nxt =~ RESONANT
elsif c =~ RESONANT && prevb == c && prev =~ VOWEL
else c
end
}.compact.join # "iskmljk"
Actually, this can be shorter:
R = /([^aeiou])[aeiou]\1/
str.chars.map.with_index { |c, i|
c unless str[i-1..i+1][R] || str[i-2..i][R]
}.compact.join # "iskmljk"
i figured since the "o" is a fix character in the sequence to be accepted, i can just go through with a count var and see if the characters next to it are equal to each other and non-vowels. also i found that slice can be also passed two parameters so that it slices starting at an offset and stopping after the given length.
index = 0
while index < str.length
index = index + 1
if str[index] == "o" and str[index-1] == str[index+1] and str[index-1] != /[^aeiou]/
str.slice!(index, 2)

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

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 ****

Resources