Coderbyte Second Great Low - code works but is rejected - ruby

I'm currently working through the Coderbyte series to get better at Ruby programming. Maybe this is just a bug in their site (I don't know), but my code works for me everywhere else besides on Coderbyte.
The purpose of the method is to return the 2nd smallest and the 2nd largest elements in any inputted array.
Code:
def SecondGreatLow(arr)
arr=arr.sort!
output=[]
j=1
i=(arr.length-1)
secSmall=''
secLarge=''
while output.length < 1
unless arr.length <= 2
#Get second largest here
while (j<arr.length)
unless arr[j]==arr[j-1]
unless secSmall != ''
secSmall=arr[j]
output.push(secSmall)
end
end
j+=1
end
#get second smallest here
while i>0
unless arr[i-1] == arr[i]
unless secLarge != ''
secLarge=arr[i-1]
output.push(secLarge)
end
end
i-=1
end
end
end
# code goes here
return output
end
# keep this function call here
# to see how to enter arguments in Ruby scroll down
SecondGreatLow(STDIN.gets)
Output
Input: [1,2,3,100] => Output: [2,3] (correct)
Input: [1,42,42,180] => Output: [42,42] (correct)
Input: [4,90] => Output: [90,4] (correct)
The problem is that I'm awarded 0 points and it tells me that my output was incorrect for every test. Yet, when I actually put any inputs in, it gives me the output that I expect. Can someone please assist with what the problem might be? Thanks!
Update
Thanks to #pjs answer below, I realized this could be done in just a few lines:
def SecondGreatLow(arr)
arr=arr.sort!.uniq
return "#{arr[1]} #{arr[-2]}"
end
# keep this function call here
# to see how to enter arguments in Ruby scroll down
SecondGreatLow(STDIN.gets)

It's important to pay close attention to the problem's specification. Coderbyte says the output should be the values separated by a space, i.e., a string, not an array. Note that they even put quotes around their "Correct Sample Outputs".
Spec aside, you're doing way too much work to achieve this. Once the array is sorted, all you need is the second element, a space, and the second-to-last element. Hint: Ruby allows both positive and negative indices for arrays. Combine that with .to_s and string concatenation, and this should only take a couple of lines.
If you are worried about non-unique numbers for the max and min, you can trim the array down using .uniq after sorting.

You need to check condition for when array contains only two elements. Here is the complete code:
def SecondGreatLow(arr)
arr.uniq!
arr.sort!
if arr.length == 2
sec_lowest = arr[1]
sec_greatest = arr[0]
else
sec_lowest = arr[1]
sec_greatest = arr[-2]
end
return "#{sec_lowest} #{sec_greatest}"
end

Related

Why does the string 'xabcx' return as a palindrome in my method? (Ruby)

I'm going through some exercises on App Academy Open, and I've come across a test case that I can't quite figure out. I've written a method to detect if a given string is a palindrome or not, and it seems to work fine for all strings I've thrown at it - apart from 'xabcx'. Am I missing something?
def palindrome?(str)
(str.length / 2).times do |idx|
if str[idx - 1] != str[idx * -1]
return false
end
end
true
end
p palindrome?("xabcx")
I expect an output of false, but I get true. The method works as expected for every other word I've tried.
As Cary Swoveland already pointed out your indexing is wrong. To make debugging easier for you the next time I would recommend printing out both the values of the indices your using as well as the actual value of the char you select from the strings for each iteration of the loop.
As an alternative and really shorter and simpler version you could also use:
def palindrome?(str)
str == str.reverse
end
or - taking into account ignoring upper / lower case:
def palindrome?(str)
str.downcase == str.downcase.reverse
end

Ruby. Range:Each change value

I'm trying to write a siple script, that calculates fibonacci numbers in a loop:
def fib(v)
return v if v < 2
(fib(v-2) + fib(v-1))
end
[0..15].each do |i|
puts "#{fib(i-1)} "
end
But this code fails because i-1 cannot be executed, as i has type Range. What should I do with it? I know, there are many other ways to calculate fibonacci numbers, but I need this code to work, not to rewrite it.
Issue: [] is the syntax for creating an array. So [0..15] creates an array with one element. That one element is the Range, 0..15. Range itself is an enumerable, so you can:
(0..15).each do |i|
puts fibonacci(i - 1)
end
As a side note, using interpolating strings in unnecessary when you have just 1 element to print.

Why is the method deleting the element inside my array?

I'm trying to create my own .sort method as an exercise in a ruby book, using recursion, and for some reason they haven't taught me the spaceship operator yet. My code works to get the smallest value - apple - and puts it in the sorted array, and it even repeats using the recursion, and resets the array to repeat the process to add the second smallest word. The problem is for some reason it removes the smallest word -apple- and I can't figure out why. I know where I think - in the else myArray.length == 1 statement when I pop the element off the array, but why is it removing from the sortedArray too?
sortedArray ends up with value apple, then when it does recursion it SHOULD be sortedArray = ['apple', 'banana' …] but it removes apple, then it removes banana etc… until I end up with sortedArray = ['quincy']
I have tried moving my arrays to multiple places, and I've tried adding to the sortedWords array in multiple places but it is always deleting or resetting the sortedWords array.
It looks like I'm really close since I've got the alphabetizing working. How do I get it to add all the items to the sortedWords array?
ArrayofWords = ['cat', 'dog', 'bat', 'elephant', 'apple', 'banana', 'quincy', 'boo']
# Why is it deleting, or replacing my sortedWords array? If you run this code you will notice that the sortedWords array
# is giving me the smallest word in the array, but then I add the recursive part, and somehow the previous smallestword
#gets deleted... but I have never in any part of my code say delete or replace the sorted array...
def sortTheArray myArray
unsortedWords = []
sortedWords = []
smallestValue = ''
while myArray.length != 0
if myArray.first < myArray.last
unsortedWords.push(myArray.last)
myArray.pop
elsif myArray.first > myArray.last
unsortedWords.push(myArray.first)
myArray.delete_at(0)
else myArray.length == 1
sortedWords.push(myArray.first)
myArray.pop # This is my problem area I think???
end # if else
#puts 'sorted words'
#puts sortedWords
#puts 'unsortedWords'
#puts unsortedWords
end # while
puts 'sorted words'
puts sortedWords
puts 'unsortedWords'
puts unsortedWords
myArray = unsortedWords
while myArray.length > 0
sortTheArray myArray
end #while
end # sortTheArray
sortTheArray ArrayofWords
most of those puts's are not necessary, I was just trying to figure out where the problem was.
You've got numerous problems with your code. For example, you seem to want to accumulate sorted words across invocations of this method, but you reinitialize sorted_words to [] at the start of the method block.
I would suggest first trying to express your recursive solution in English as simply as possible and then seek to implement it.
For example, the following is an approach which seems to be in line with what you are trying to do:
def sorted_array(array)
lowest_value prepended to the sorted_value of the array with the lowest_value removed
end
I'm sharing the above because it appears that you're new to Ruby and just implementing the above in an idiomatic fashion will be a good challenge.

Remove string values that differ only in case from an array (Ruby)

So, here's the preface:
I'm quite a beginner in Ruby. I'm working on a anagram finding script (find all anagrams in a text file). The essence is: I create a dictionary where key is word code and value is an array with words that refer to this code. It is like that: {"abdeis"=>["abides", "biased"] ,"achr"=>["char"], ... }. In the end I just print out the values with length of >1. So good so far.
Here's the deal: I want to modify the output, so that such cases are omitted: ["Wood", "wood", "WooD"] - all are different in case only. But such cases should stay: ["Doom", "DooM", "mood"].
My current piece of code:
def print_anagram(anagram_dict)
anagram_list = anagram_dict.values
anagram_list.each { |i|
if i.length > 1
print i.join("\t")
print "\n"
else
next
end
}
end
anagram_dict is a dictionary mentioned above.
What checks should I make to throw these cases away? The things I think of seem way to bulky to me. Thanks in advance!
def print_anagram(anagram_dict)
anagram_list = anagram_dict.values
anagram_list.each do |i|
next if i.map(&:downcase).uniq.length == 1
if i.length > 1
print i.join("\t")
print "\n"
else
next
end
end
end
What this does:
make all Strings lowercase
get only unique elements from the array
if you only have one unique element, all elements are the same
map(&:downcase) is a shorter way of doing: map { |element| element.downcase }
Does this do what you want?
def print_anagram(anagram_dict)
anagram_list = anagram_dict.values.uniq{|word| word.downcase}
# rest of your code

Explaining a Ruby code snippet

I'm in that uncomfortable position again, where somebody has left me with a code snippet in a language I don't know and I have to maintain it. While I haven't introduced Ruby to myself some parts of it are quite simple, but I'd like to hear your explanations nonetheless.
Here goes:
words = File.open("lengths.txt") {|f| f.read }.split # read all lines of a file in 'words'?
values = Array.new(0)
words.each { |value| values << value.to_i } # looked this one up, it's supposed to convert to an array of integers, right?
values.sort!
values.uniq!
diffs = Array.new(0) # this looks unused, unless I'm missing something obvious
sum = 0
s = 0 # another unused variable
# this looks like it's computing the sum of differences between successive
# elements, but that sum also remains unused, or does it?
values.each_index { |index| if index.to_i < values.length-1 then sum += values.at(index.to_i + 1) - values.at(index.to_i) end } # could you also explain the syntax here?
puts "delta has the value of\n"
# this will eventually print the minimum of the original values divided by 2
puts values.at(0) / 2
The above script was supposed to figure out the average of the differences between every two successive elements (integers, essentially) in a list. Am I right in saying this is nowhere near what it actually does, or am I missing something fundamental, which is likely considering I have no Ruby knowledge?
Explanation + refactor (non used variables removed, functional approach, each_cons):
# Read integer numbers from file, sort them ASC and remove duplicates
values = File.read("lengths.txt").split.map(&:to_i).sort.uniq
# Take pairwise combinations and get the total sum of partial differences
partial_diffs = values.each_cons(2).map { |a, b| b - a }.inject(0, :+)
That guy surely didn't grasp Ruby himself. I wonder why he chose to use that language.
Here's an annotated explanation:
# Yes, it reads all lines of a file in words (an array)
words = File.open("lengths.txt") {|f| f.read }.split
values = Array.new(0)
# Yes, to_i convert string into integer
words.each { |value| values << value.to_i }
values.sort!
values.uniq!
# diffs and s seem unused
diffs = Array.new(0)
sum = 0
s = 0
# The immediate line below can be read as `for(int index = 0; index < values.length; index++)`
values.each_index { |index|
# index is integer, to_i is unnecessary
if index.to_i < values.length-1 then
# The `sum` variable is used here
# Following can be rewritten as sum += values[i-1] - values[i]
sum += values.at(index.to_i + 1) - values.at(index.to_i)
end
}
puts "delta has the value of\n"
# Yes, this will eventually print the minimal of the original values divided by 2
puts values.at(0) / 2
To help you get a better grasp of what "real" (idiomatic) Ruby looks like, I've written what you wanted, with some annotations
values = open("lengths.txt") do |f|
# Read it like this:
#
# Take the list of all lines in a file,
# apply a function to each line
# The function is stripping the line and turning it
# into an integer
# (This means the resultant list is a list of integers)
#
# And then sort it and unique the resultant list
#
# The eventual resultant list is assigned to `values`
# by being the return value of this "block"
f.lines.map { |l| l.strip.to_i }.sort.uniq
end
# Assign `diffs` to an empty array (instead of using Array.new())
diffs = []
values.each_index do |i|
# Syntactic sugar for `if`
# It applies the 1st part if the 2nd part is true
diffs << (values[i+1] - values[i]) if i < values.length - 1
end
# You can almost read it like this:
#
# Take the list `diffs`, put all the elements in a sentence, like this
# 10 20 30 40 50
#
# We want to inject the function `plus` in between every element,
# so it becomes
# 10 + 20 + 30 + 40 + 50
#
# The colon `:+` is used to refer to the function `plus` as a symbol
#
# Take the result of the above summation, divided by length,
# which gives us average
delta = diffs.inject(:+) / diffs.length
# `delta` should now contains the "average of differences" between
# the original `values`
# String formatting using the % operator
# No \n needed since `puts` already add one for us
puts "delta has the value of %d" % delta
That is by no means pushing the true power of Ruby, but you see why Rubyists get so enthusiastic about expressiveness and stuffs :P
values.each_index { |index| if index.to_i < values.length-1 then sum += values.at(index.to_i + 1) - values.at(index.to_i) end }
The above line sums the differences between consecutive values. the test index.to_i < values.length-1 is to not access the array out of bounds, because of values.at(index.to_i + 1).
You are right, this code does not do much thing. it only prints half of the minimum value from the file.

Resources