Counting X's and O's in a string in Ruby - 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

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!

Ruby FizzBuzz using arrays, my logic seems right but it is getting an error

FizzBuzz, a classic problem, returns all the numbers up to N with a slight twist. If a number is divisible by 3, it is replaced with "fizz". If it's divisible by 5, it's replaced with "buzz". If it's divisible by both, it's replaced with "fizzbuzz"
I keep getting this Error message:
comparison of Fixnum with nil failed
Can someone explain this error message to me please? Also why is the code not working?
def fizz_buzz(n)
arr = (1..n).to_a
i = 0
while arr[i] < arr[n]
if i % 3 == 0 && i % 5 == 0
arr[i] = 'fizzbuzz'
elsif i % 3 == 0
arr[i] = 'fizz'
elsif i % 5 == 0
arr[i] = 'buzz'
else
arr[i] = i
i += 1
end
end
return arr
end
fizz_buzz(12)
Your conditions are just a bit off, give this a try:
def fizz_buzz(n)
arr = (1..n).to_a
i = 0
while i < n
if arr[i] % 3 == 0 && arr[i] % 5 == 0
arr[i] = 'fizzbuzz'
elsif arr[i] % 3 == 0
arr[i] = 'fizz'
elsif arr[i] % 5 == 0
arr[i] = 'buzz'
end
i+=1
end
return arr
end
Trying to access arr[n] puts you outside the bounds of the array which returns nil in Ruby.
You can update the code the ruby way, by using blocks and guards, I don't remember last time I used a while loop in ruby :)
Also, Array.new accepts a block as an argument which you can exploit to build your Array in a single step:
def fizz_buzz(n)
Array.new(n) do |index|
x = index + 1
case
when x % 3 == 0 && x % 5 == 0 then "fizzbuzz"
when x % 3 == 0 then "fizz"
when x % 5 == 0 then "buzz"
else x
end
end
end
Notice I used 1 as a base index and not 0, you can just remove x = index + 1 and replace x with index to have it working in a zero index base
A solution with a block instead of the while loop, and guards
def fizz_buzz(n)
arr = (1..n).to_a
0.upto(n - 1) do |i|
arr[i] = "fizzbuzz" and next if i % 3 == 0 && i % 5 == 0
arr[i] = "fizz" and next if i % 3 == 0
arr[i] = "buzz" if i % 5 == 0
end
arr
end
#brad-melanson beat me to the straight-forward answer to your question, so I'll share an answer which uses some common Ruby idioms (passing a range to the Array constructor and map), which simplify things, prevent you from having to do any iteration bookkeeping and prevent the possibility of off-by-one errors, out-of-bounds errors, etc.
def fizz_buzz(n)
Array(1..12).map do |n|
if n % 3 == 0 && n % 5 == 0
'fizzbuzz'
elsif n % 3 == 0
'fizz'
elsif n % 5 == 0
'buzz'
else
n
end
end
end
result = fizz_buzz 12
# result => [1, 2, "fizz", 4, "buzz", "fizz", 7, 8, "fizz", "buzz", 11, "fizz"]

Code doesn't work in coderbytes but it works in Cloud9

In Cloud9 I use the following code and it works.
def LongestWord(sen)
i = 0
cha ="&#%*^$!~(){}|?<>"
new = ""
while i < sen.length
i2 = 0
ch = false
while i2 < cha.length
if sen[i] == cha[i2]
ch = true
end
i2 += 1
end
if ch == false
new += sen[i].to_s
end
i += 1
end
words = new.split(" ")
longest = ""
idx = 0
count = 0
while idx < words.length
word = words[idx]
if word.length > count
longest = word
count = word.length
end
idx += 1
end
# code goes here
return longest
end
# keep this function call here
# to see how to enter arguments in Ruby scroll down
LongestWord("beautifull word")
In Codebytes in the exercise "Longest Word" you have to use the same STDIN in the arguments. It is the same code but changing the argument but it doesn't work:
def LongestWord(sen)
i = 0
cha ="&#%*^$!~(){}|?<>"
new = ""
while i < sen.length
i2 = 0
ch = false
while i2 < cha.length
if sen[i] == cha[i2]
ch = true
end
i2 += 1
end
if ch == false
new += sen[i].to_s
end
i += 1
end
words = new.split(" ")
longest = ""
idx = 0
count = 0
while idx < words.length
word = words[idx]
if word.length > count
longest = word
count = word.length
end
idx += 1
end
# code goes here
return longest
end
# keep this function call here
# to see how to enter arguments in Ruby scroll down
LongestWord(STDIN.gets)
I think may be something is creating some kind of conflict with the browser. The output shows a lot of numbers. Can some one help me testing the code?. Any feedback is appreciated, thanks!
Coderbyte is running your code on an old version of Ruby - Ruby 1.8.7
In this version of Ruby, using an index into a string like sen[i] doesn't return the character at i, it returns the numeric ASCII value of that character instead. That's where the numbers are coming from.
To get the code to work on Ruby 1.8.7 you can replace some_string[i] with some_string[i, 1] - this variation returns the substring of length 1 starting at i so is the same as the behaviour of some_string[i] in more recent Ruby versions. See the docs here for more details.

Ruby longest palindrome - why does a slight modification in a while loop break my code?

I'm trying to solve this Ruby problem and I can't figure out why having a minor while loop difference renders one test false: longest_palindrome("abba") outputs "bb" instead of "abba", which is false. I can only solve it with for and while loops, so please no advanced methods. It's easier to highlight the difference in the code block (first one is the working solution, second is mine. Also assume the palindrome? method is already defined):
def longest_palindrome(string)
best_palindrome = nil
idx1 = 0
***while idx1 < string.length
length = 1
while (idx1 + length) <= string.length
substring = string.slice(idx1, length)***
if palindrome?(substring) && (best_palindrome == nil || substring.length > best_palindrome.length)
best_palindrome = substring
end
length += 1
end
idx1 += 1
end
return best_palindrome
end
def longest_palindrome(string)
longest = nil
i = 0
***while i < string.length
i2 = 1
while i2 < string.length***
if palindrome?(string.slice(i, i2)) == true && (longest == nil || string.slice(i, i2).length > longest.length)
longest = string.slice(i, i2)
end
i2 += 1
end
i += 1
end
return longest
end
This part of your code...
while i2 < string.length
... means you're never checking the maximum possible length.
"abba".slice(0,4) is the entire string, but you only ever go up to "abba".slice(0,3) which is "abb".
So you never test the entire string.
Change the line to...
while i2 <= string.length
...and it should be ok.

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