Longest Run Program In Ruby - 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!

Related

How to write nested if/then statements in 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"

Counting X's and O's in a string in Ruby

I'm not sure why my code is not working, I think my logic is right?
Have the function ExOh(str) take the str parameter being passed and return the string true if there is an equal number of x's and o's, otherwise return the string false. Only these two letters will be entered in the string, no punctuation or numbers. For example: if str is "xooxxxxooxo" then the output should return false because there are 6 x's and 5 o's.
ExOh(str)
i = 0
length = str.length
count_x = 0
count_o = 0
while i < length
if str[i] == "x"
count_x += 1
elsif str[i] == "o"
count_o += 1
end
i+=1
end
if (count_o == count_x)
true
elsif (count_o != count_x)
false
end
end
The problem with your code is the function declaration. Use def ExOh(str) at the start. It may help if you indented also.
def ExOh(str)
i = 0
length = str.length
count_x = 0
count_o = 0
while i < length
if str[i] == "x"
count_x += 1
elsif str[i] == "o"
count_o += 1
end
i+=1
end
if (count_o == count_x)
true
elsif (count_o != count_x)
false
end
end
By the way, a simpler solution using the standard library #count https://ruby-doc.org/core-2.2.0/String.html#method-i-count
def ExOh(str)
str.count('x') == str.count('o')
end

why does this ruby code not return anything?

I'm trying to take a number and return a string with dashes around any odd numbers. Also, the string should not begin or end with a dash.
I've written the following but it does not return anything:
def dasherize_number(num)
string = num.to_s
i = 0
while i<string.length
if (string[i].to_i % 2) != 0
string[i] = '-' + string[i] + '-'
end
i += 1
end
if string[0] == '-'
string.pop(1)
end
if (string.length - 1) == '-'
string.pop(1)
end
string
end
It appears to be looping infinitely if I understand correctly; the console shows no output and doesn't allow me to do anything else unless I refresh. I've reviewed the code by each character, but I can't figure where it goes wrong.
There were a lot of logical issues in your code.
here's something that might just work for you
def dasherize_number(num)
string = num.to_s
str_len = string.length
i = 0
while i < str_len
next if string[i] == '-'
if (string[i].to_i % 2) != 0
string[i] = '-' + string[i] + '-'
str_len = string.length
i += 3
else
i += 1
end
end
if string[0] == '-'
string = string[1..-1]
end
if (string[string.length - 1]) == '-'
string = string[0..-2]
end
string.gsub('--', '-')
end
Explaination
Firstly, you had this condition in your while loop i < string.length
Which wouldn't work, because the length of the string keeps changing. So i've used a variable to store the value and update the variable if the string is updated.
If the string is updated, we can be sure that we can skip the next two indexes.
eg: number inputed -> 122
then after first iteration the string would be -1-22
so we don't want to run the same condition for the next index because, that would be 1 again, hence the infinite loop. (Hope you get the idea)
pop wouldn't work on string, just because we can access characters using indexes like for arrays, we can't use pop for strings.
To make sure there are no consecutive dashes, i've used gsub to replace them with single dash.
The problem seems to be in this part of the code:
while i<string.length
if (string[i].to_i % 2) != 0
string[i] = '-' + string[i] + '-'
end
i += 1
end
If your string contains odd number it increases its length by 2 more chars (2x-), but incrementing it by 1 (i+=1).
Assign initial string length to a var and check its length in the while loop.
string_length = string.length
while i < string_length
if ((string[i].to_i % 2) != 0)
string[i] = '-' + string[i] + '-'
end
i += 1
end

Both tests are returning false even though in my mind the code executes perfectly

# Write a method that takes in a string. Your method should return the
# most common letter in the array, and a count of how many times it
# appears.
#
# Difficulty: medium.
def most_common_letter(string)
letter = 0
letter_count = 0
idx1 = 0
mostfreq_letter = 0
largest_letter_count = 0
while idx1 < string.length
letter = string[idx1]
idx2 = 0
while idx2 < string.length
if letter == string[idx2]
letter_count += 1
end
idx2 += 1
end
if letter_count > largest_letter_count
largest_letter_count = letter_count
mostfreq_letter = letter
end
idx1 += 1
end
return [mostfreq_letter, largest_letter_count]
end
# These are tests to check that your code is working. After writing
# your solution, they should all print true.
puts(
'most_common_letter("abca") == ["a", 2]: ' +
(most_common_letter('abca') == ['a', 2]).to_s
)
puts(
'most_common_letter("abbab") == ["b", 3]: ' +
(most_common_letter('abbab') == ['b', 3]).to_s
)
So in my mind the program should set a letter and then once that is set cycle through the string looking for letters that are the same, and then once there is one it adds to letter count and then it judges if its the largest letter count and if it is those values are stored to the eventual return value that should be correct once the while loop ends. However I keep getting false false. Where am I going wrong?
Your code does not return [false, false] to me; but it does return incorrect results. The hint by samgak should lead you to the bug.
However, for a bit shorter and more Rubyish alternative:
def most_common_letter(string)
Hash.new(0).tap { |h|
string.each_char { |c| h[c] += 1 }
}.max_by { |k, v| v }
end
Create a new Hash that has a default value of 0 for each entry; iterate over characters and count the frequency for each of them in the hash; then find which hash entry is the largest. When a hash is iterated, it produces pairs, just like what you want for your function output, so that's nice, too.

Number of repeats

Write a method that takes in a string and returns the number of letters that appear more than once in the string. You may assume the string contains only lowercase letters. Count the number of letters that repeat, not the number of times they repeat in the string.
I implemented methods and test cases as:
def num_repeats(string)
count = 0
dix = 0
new = ""
while dix < string.length
letter = string[dix]
if !(new.include?(letter))
new = new + "letter"
else
break
end
dix2 = dix + 1
while dix2 < string.length
if letter == string[dix2]
count +=1
break
end
dix2 +=1
end
dix += 1
end
puts(count.to_s)
return count
end
# These are tests to check that your code is working. After writing
# your solution, they should all print true.
puts('num_repeats("abdbc") == 1: ' + (num_repeats('abdbc') == 1).to_s)
# one character is repeated
puts('num_repeats("aaa") == 1: ' + (num_repeats('aaa') == 1).to_s)
puts('num_repeats("abab") == 2: ' + (num_repeats('abab') == 2).to_s)
puts('num_repeats("cadac") == 2: ' + (num_repeats('cadac') == 2).to_s)
puts('num_repeats("abcde") == 0: ' + (num_repeats('abcde') == 0).to_s)
Test results:
1
num_repeats("abdbc") == 1: true
2
num_repeats("aaa") == 1: false
2
num_repeats("abab") == 2: true
2
num_repeats("cadac") == 2: true
0
num_repeats("abcde") == 0: true
For the second test that returned false, what was wrong with my code?
You are appending "letter", rather than the letter variable to new on line 8.
if !(new.include?(letter))
new = new + "letter"
else
#...
end
becomes:
unless new.include?(letter)
new = new + letter
else
#...
end

Resources