How to write nested if/then statements in Ruby - ruby

I'm supposed to
define a method, three_digit_format(n), that accepts an integer, n, as an argument. Assume that n < 1000. Your method should return a string version of n, but with leading zeros such that the string is always 3 characters long.
I have been tinkering with versions of the below code, but I always get errors. Can anyone advise?
def three_digit_format(n)
stringed = n.to_s
stringed.size
if stringed.size > 2
return stringed
end
elsif stringed > 1
return "0" + stringed
end
else
return "00" + stringed
end
end
puts three_digit_format(9)

rjust
You could just use rjust:
n.to_s.rjust(3, '0')
If integer is greater than the length of str, returns a new String of
length integer with str right justified and padded with padstr;
otherwise, returns str.
Your code
Problem
If you let your text editor indents your code, you can notice there's something wrong:
def three_digit_format(n)
stringed = n.to_s
stringed.size
if stringed.size > 2
return stringed
end
elsif stringed > 1 # <- elsif shouldn't be here
return "0" + stringed
end
else
return "00" + stringed
end
end
puts three_digit_format(9)
Solution
if, elsif and else belong to the same expression : there should only be one end at the end of the expression, not for each statement.
def three_digit_format(n)
stringed = n.to_s
if stringed.size > 2
return stringed
elsif stringed.size > 1
return "0" + stringed
else
return "00" + stringed
end
end
puts three_digit_format(9)
# 009

This function, as some have pointed out, is entirely pointless since there's several built-in ways of doing this. Here's the most concise:
def three_digit_format(n)
'%03d' % n
end
Exercises that force you to re-invent tools just drive me up the wall. That's not what programming is about. Learning to be an effective programmer means knowing when you have a tool at hand that can do the job, when you need to use several tools in conjunction, or when you have no choice but to make your own tool. Too many programmers jump immediately to writing their own tools and overlook more elegant solutions.
If you're committed to that sort of approach due to academic constraints, why not this?
def three_digit_format(n)
v = n.to_s
while (v.length < 3)
v = '0' + v
end
v
end
Or something like this?
def three_digit_format(n)
(n + 1000).to_s[1,3]
end
Where in that case values of the form 0-999 will be rendered as "1000"-"1999" and you can just trim off the last three characters.
Since these exercises are often absurd, why not take this to the limit of absurdity?
def three_digit_format(n)
loop do
v = Array.new(3) { (rand(10) + '0'.ord).chr }.join('')
return v if (v.to_i == n)
end
end
If you're teaching things about if statements and how to append elsif clauses, it makes sense to present those in a meaningful context, not something contrived like this. For example:
if (customer.exists? and !customer.on_fire?)
puts('Welcome back!')
elsif (!customer.exists?)
puts('You look new here, welcome!')
else
puts('I smell burning.')
end
There's so many ways a chain of if statements is unavoidable, it's how business logic ends up being implemented. Using them in inappropriate situations is how code ends up ugly and Rubocop or Code Climate give you a failing grade.

As others have pointed out, rjust and applying a format '%03d' % n are built in ways to do it.
But if you have to stick to what you've learned so far, I wonder if you've been introduced to the case statement?
def three_digit_format(n)
case n
when 0..9
return "00#{n}"
when 10..99
return "0#{n}"
when 100..999
return "#{n}"
end
end
I think it's cleaner than successive if statements.

Here's my spin on it:
def three_digit_format(n)
str = n.to_s
str_len = str.length
retval = if str_len > 2
str
elsif str_len > 1
'0' + str
else
'00' + str
end
retval
end
three_digit_format(1) # => "001"
three_digit_format(12) # => "012"
three_digit_format(123) # => "123"
Which can be reduced to:
def three_digit_format(n)
str = n.to_s
str_len = str.length
if str_len > 2
str
elsif str_len > 1
'0' + str
else
'00' + str
end
end
The way it should be done is by taking advantage of String formats:
'%03d' % 1 # => "001"
'%03d' % 12 # => "012"
'%03d' % 123 # => "123"

Related

Longest Run Program In Ruby

I have a school assignment where i have to find the longest run of adjacent equal characters in a given string with Ruby. My program works fine without the last loop, but once i added it gave me the error:
(repl):47: syntax error, unexpected keyword_end
(repl):53: syntax error, unexpected end-of-input, expecting keyword_end
puts longestRun
^
Here is my Code
puts 'What is your string?'
givenString = gets.chomp
def maxBlock(str)
maxRun = 0
currentRun = 1
characterCounter = 1
if str.length == 0
maxRun = 0
#If no input, longest run is zero
elsif str.length == 1
maxRun = 1
#If string is one character, longest run is 1
elsif str.length == 2 and str[characterCounter] != str[characterCounter + 1]
maxRun = 1
#if string is two chars and they do not equal, longest run is 1
elsif str.length == 3 and str[0] != str[1] and str[1] != str[2]
maxRun = 1
#if string is three chars and they do not equal, longest run is 1
else
str.each_char do|st|
#Go through each char, compare it to the next, find longest run
if st == str[characterCounter]
currentRun++
if currentRun > maxRun
maxRun = currentRun
end
else
currentRun = 1
end
characterCounter++
end
end
end
longestRun = maxBlock(givenString)
puts longestRun
EDIT: I am a highschool student, and only have a base knowledge of programming.
EDIT: I just made a few stupid mistakes. I appreciate everyone's help. Here is my working program without the use of anything too complicated.
puts 'What is your string?'
givenString = gets.chomp
def maxBlock(str)
maxRun = 0
currentRun = 1
characterCounter = 0
if str.length == 0
maxRun = 0
#If no input, longest run is zero
elsif str.length == 1
maxRun = 1
#If string is one character, longest run is 1
elsif str.length == 2 and str[characterCounter] != str[characterCounter + 1]
maxRun = 1
#if string is two chars and they do not equal, longest run is 1
elsif str.length == 3 and str[0] != str[1] and str[1] != str[2]
maxRun = 1
#if string is three chars and they do not equal, longest run is 1
else
characterCounter += 1
str.each_char do|st|
#Go through each char, compare it to the next, find longest run
if st == str[characterCounter]
currentRun += 1
if currentRun > maxRun
maxRun = currentRun
end
else
currentRun = 1
end
characterCounter += 1
end
end
return maxRun
end
longestRun = maxBlock(givenString)
puts longestRun
String Scans and Sorting
There are algorithms for this, but Ruby offers some nice shortcuts. For example:
def longest_string str
str.scan(/((\p{Alnum})\2+)/).collect { |grp1, grp2| grp1 }.sort_by(&:size).last
end
longest_string 'foo baaar quuuux'
#=> "uuuu"
This basically just captures all runs of repeated characters, sorts the captured substrings by length, and then returns the last element of the length-sorted array.
Secondary Sorting
If you want to do a secondary sort, such as first by length and then by alphabetical order, you could replace Enumerable#sort_by with the block form of Enumerable#sort. For example:
def longest_string str
str.scan(/((\p{Alnum})\2+)/).
collect { |grp1, grp2| grp1 }.
sort {|a, b| [a.size, a] <=> [b.size, b] }.
last
end
longest_string 'foo quux baar'
#=> "uu"
This is one way you could do it.
str = "111 wwwwwwwwaabbbbbbbbbbb$$$$****"
r = /
(.) # Match any character in capture group 1
\1* # Match the contents of capture group 1 zero or more times
/x # Free-spacing regex definition mode
str.gsub(r).max_by(&:size)
#=> "bbbbbbbbbbb"
I used the form of String#gsub without a second argument or block, as that returns an enumerator that generates the strings matched by the regex. I then chained that enumerator to the method Enumerable#max_by to find the longest string of consecutive characters. In other words, I used gsub merely to generate matches rather than to perform substitutions.
One could of course write str.gsub(/(.)\1*/).max_by(&:size).
Here is a simplified version that should work in all cases:
puts 'What is your string?'
given_string = gets.chomp
def max_block(str)
max_run = 0
current_run = 1
str.each_char.with_index do |st, idx|
if st == str[idx + 1]
current_run += 1
else
current_run = 1
end
max_run = current_run if current_run > max_run
end
max_run
end
longest_run = max_block(given_string)
puts longest_run
You were on the right track but Ruby can make things a lot easier for you. Notice how with_index gets rid of a lot of the complexity. Iterators, oh yeah.
I also changed your method name and variables to camel_case.
Happy coding!

NoMethodError with .chr.to_i

I'm trying to create a recursive method sum_of_digits(i) that takes the sum of integers, i.e. '456' = 4+5+6 = 15
However, I receive a NoMethodError for chr.to_i in the following code:
def sum_of_digits(i)
input = i.to_s
if i == 0
return 0
elsif input.length == 1
return i
else
for n in 1..input.length
sum += input[i].chr.to_i % 10^(n-1)
end
end
return sum
end
Thank you!
String indexes are zero-based in ruby. The problem is here:
for n in 1..input.length
it should be written as
for n in 0..input.length-1
BTW, call to chr is superfluous as well, since you already have a string representation of a digit there. As well, sum must be declared in advance and set to zero.
Also, the whole code is not ruby idiomatic: one should avoid using unnecessary returns and for-loop. The modified version (just in case) would be:
def sum_of_digits(i)
input = i.to_s
case
when i == 0 then 0 # return zero
when input.length == 1 then i # return i
else
sum = 0
input.length.times do |index|
sum += input[index].to_i % 10^index
end
sum
end
end
or, even better, instead of
sum = 0
input.length.times do |index|
sum += input[index].to_i % 10^index
end
sum
one might use inject:
input.length.times.inject(0) do |sum, index|
sum += input[index].to_i % 10^index
end

Ruby - App Academy Practice Exercise About Condition in While Loop

I'm completing App Academy's practice problems for the first coding challenge and have a question regarding the solution provided for #8 nearby az:
# Write a method that takes a string in and returns true if the letter
# "z" appears within three letters **after** an "a". You may assume
# that the string contains only lowercase letters.
#
# Difficulty: medium.
def nearby_az(string)
idx1 = 0
while idx1 < string.length
if string[idx1] != "a"
idx1 += 1
next
end
idx2 = idx1 + 1
while (idx2 < string.length) && (idx2 <= idx1 + 3)
if string[idx2] == "z"
return true
end
idx2 += 1
end
idx1 += 1
end
return false
end
# These are tests to check that your code is working. After writing
# your solution, they should all print true.
puts("\nTests for #nearby_az")
puts("===============================================")
puts('nearby_az("baz") == true: ' + (nearby_az('baz') == true).to_s)
puts('nearby_az("abz") == true: ' + (nearby_az('abz') == true).to_s)
puts('nearby_az("abcz") == true: ' + (nearby_az('abcz') == true).to_s)
puts('nearby_az("a") == false: ' + (nearby_az('a') == false).to_s)
puts('nearby_az("z") == false: ' + (nearby_az('z') == false).to_s)
puts('nearby_az("za") == false: ' + (nearby_az('za') == false).to_s)
puts("===============================================")
In the second while loop:
while (idx2 < string.length) && (idx2 <= idx1 + 3)
why is the condition (idx2 < string.length) necessary? I tested the code without it and got the same results.
Thank you for your assistance.
why is the condition (idx2 < string.length) necessary?
It is not necessary. It's a guard to prevent meaningless iterations of the loop, when idx2 goes out of bounds of the string.
Addressing characters at position beyond string's length will return nil. nil will never be equal to 'z'. So we might as well just stop when we reach the end. This is what the check is for here, optimization.
In other situations, out-of-bounds access often is a serious offense and leads to all kinds of problems (crashes, usually). So it makes sense to always do this.
I know this does not answer your exact question, and other people have answered it already, but as the usual case with programming, there's a better way. You can easily solve this with regular expression
def nearby_az(string)
!(string =~ /a\w{0,3}z/).nil?
end
The regex will match the pattern a, with 0 to 3 characters after it, then a z. If this matches nothing, the =~ operator will return nil, so nil? method returns true, meaning the string does not have nearby az, so we use the ! to invert the boolean, and this method will return false.
If there is a match, =~ returns the index of the first characters, which is not nil, so nil? return false, and we inverted it as before to return true.
Just thought this may be helpful.

Ruby - Prime Number calculator

I need some feedback to figure out why I cant puts or print anything from my methods on the screen. This is a simple script I wrote to solve the problem of finding the 1001st prime number. Thanks
def primes
# iterates through numbers until it has the 1001th prime number and returns it.
# I chose to create the num_primes variable instead of counting the number of
# elements in in_prime_array every iteration
# based upon a guess that it would be faster to check.
is_prime_array = []
num_primes = 0
i = 2
loop do
is_prime_array << i && num_primes += 1 if is_prime?(i) == true
i += 1
break if num_primes == 1001
end
is_prime_array[1001]
end
def is_prime? (num)
# Checks to see if the individual number given is a prime number or not.
i = 2
loop do
if i == num
return true
elsif num % i == 0
return false
else
i += 1
end
end
end
Thanks for any help!
EDIT
I took your advice and tried this pice of code:
def is_prime? (num)
# Checks to see if the individual number given is a prime number or not.
i = 2
loop do
if i == num
return true
elsif num % i == 0
return false
else
i += 1
end
end
end
i = 0
count = 0
loop do
count += 1 if is_prime?(x)
puts "#{i}" if count == 1001
break
end
It still returns nothing. Hummm
i = 0
count = 0
loop do
if is_prime(i)
count += 1
end
if count == 10001
puts "#{i}"
break
end
end
Simple method :)
It's an off-by-one error. If you have 1001 elements in an array, the last element will be at index 1000.
Where you have
is_prime_array[1001]
Change it to
is_prime_array[1000]
And you can do this:
puts primes
=> 7927
You could also have
is_prime_array.last
instead of a specific index number.
What are you trying to "puts"? The first thing I notice is that there is no call to primes in the file, so nothing will happen if you try to run this code by itself. Maybe that's why you don't see anything printed.
Here's an example of how to print a few variables inside your loop:
loop do
...
puts "At iteration #{i}, we have prime=#{is_prime?(i)}"
If you don't know, enclosing a statement with #{<statement goes here>} inside a string is the same as appending the return value of <statement goes here> to the string at that position. This is the same as "Str " + blah + " rest of str" in a language like Java.

Why doesn't my conversion code for roman numbers ('mcmxcix' for e.g) to real numbers work?

I want to convert Roman numerals, such as "mcmxcix", to arabic integers like "1999".
My code looks like:
#~ I = 1 V = 5 X = 10 L = 50
#~ C = 100 D = 500 M = 1000
def roman_to_integer roman
len = roman.length
x = 1
while x <= len
arr = Array.new
arr.push roman[x]
x += 1
end
num = 0
arr.each do |i|
if i == 'I'
num += 1
elsif i == 'V'
num += 5
elsif i == 'X'
num += 10
elsif i == 'L'
num += 50
elsif i == 'C'
num += 100
elsif i == 'D'
num += 500
elsif i == 'M'
num += 1000
end
end
num
end
puts(roman_to_integer('MCMXCIX'))
The output is 0, but I don't understand why?
Ruby doesn't have a post-increment operator. When it sees ++ it interprets that as one infix + followed by one prefix (unary) +. Since it expects an operand to follow after that, but instead finds the keyword end, you get a syntax error.
You need to replace x++ with x += 1.
Furthermore note that x isn't actually in scope inside the roman_to_integer method (which isn't a syntax error, but nevertheless wrong).
Additionally you'll have to replace all your ifs except the first with elsifs. The way you wrote it all the ifs are nested, which means that a) you don't have enough ends and b) the code doesn't have the semantics you want.
You are missing a closing parentheses so
puts(roman_to_integer('mcmxcix')
should be
puts roman_to_integer('mcmxcix')
or
puts(roman_to_integer('mcmxcix'))
The arr keeps getting annihilated in your while loop, and it is not in the scope outside of the loop. Move the following line above the while statement:
arr = Array.new

Resources