Please explain this Ruby method that returns number of repeat letters - ruby

Can anybody please explain this code? I don't understand specifically:
elsif idx2 > idx1
is_repeat = true
end
Why are we comparing indices to determine if a letter has been
repeated?
Also what does the 'next' term do inside of the if statement?
The full code is shown below:
# 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.
#
# Difficulty: hard.
def num_repeats(array)
repeats = 0
idx1 = 0
while idx1 < array.length
is_repeat = false
idx2 = 0
while idx2 < array.length
if array[idx1] != array[idx2]
idx2 += 1
next
elsif idx2 < idx1
# will have previously counted this repeat
break
elsif idx2 > idx1
is_repeat = true
end
idx2 += 1
end
if is_repeat
repeats += 1
end
idx1 += 1
end
return repeats
end

If both conditions before (if idx2 < array.length and elsif idx2 < idx1) are false but idx2 > idx1 is true, than set the local variable is_repeat to true...

Related

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.

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.

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.

Ruby's 'next' keyword

Simple question for the following set of codes:
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
What does the word "next" do in the line with the commented arrow? And is "next" a method? If not, what's the correct technical jargon for this? Cheers
next is not a method, it's a keyword. It applies to the outer while loop in your example, it stops current iteration and 'calls' next one.

Different ways of defining the conditions in a while loop. (Ruby)

I wrote a function which works. There is one part in there and it can be written in an alternative manner, but the alternative way of writing it doesn't work, although it should represent the same thing as the original code I wrote. I'd like to know why the alternative way of writing it does not work. When I ran it with the alternative codes, I think it became an infinite loop because there were no outputs.
This is a snippet of the function (The code in question is commented as "#ALTERNATIVE", there are four lines in total):
if idx2 > idx
idx3 = idx2 - 1
while idx3 >= idx #ALTERNATIVE 1: idx3 > idx
if arr[idx3] > arr[idx]
return idx3
elsif idx3 == idx #ALTERNATIVE 1: idx3 == idx + 1 or idx3 - 1 == idx
return idx2
end
idx3 -= 1
end
elsif idx > idx2
idx4 = idx2 + 1
while idx4 <= idx #ALTERNATIVE 2: idx4 < idx
if arr[idx4] > arr[idx]
return idx4
elsif idx4 == idx #ALTERNATIVE 2: idx4 == idx - 1 or idx4 + 1 == idx
return idx2
end
idx4 += 1
end
end
Your alternate code doesn't work in the cases where idx and idx2 are within 1 of each other.
Imagine idx == 1 and idx2 == 2.
if idx2 > idx
idx3 = idx2 - 1 # idx3 is equal to 1 and therefore equal to `idx`
while idx3 > idx # this is false, the while loop is not run
And nothing is returned. The same type of situation would be true for the elsif clause if idx == 2 and idx2 == 1 where the while loop would never run.
I don't know why the alternative code doesn't work, perhaps you've put in an idx2 equal to idx and it exited.
However, I thought it might be prudent to show you how to write this code more idiomatically:
def furthest_number_higher_than_base(arr, base, start)
range = start < base ? start..base : (base..start).to_a.reverse
range.each { |i| return i if arr[i] > arr[base] }
start
end

Resources