Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Ruby The Solution

Maximiliane Quel
PLUS
Maximiliane Quel
Courses Plus Student 55,489 Points

Improving the script for the user prompted average?

Hi,

I have tried the extra credit question and am would like to know if anyone can make it even more efficient? It would be really great to have feedback and/or to see other solutions to learn from them.

Here is my solution:

def user_answer
    print "Please enter a number (or press another key to finish): "
    gets.chomp
end

def numeric? test
    #ternary operator: expression ? if-true : if-false
    test.match(/\d/) == nil ? false : true
end


def create_arr
    arr = []
    input = user_answer

    while numeric? input
        arr = arr.push input.to_f
        input = user_answer
    end

    return arr
end

x = create_arr

sum = x.inject 0, &:+
average = sum / x.length

puts "You entered the numbers #{x}. The average is #{average}."

Thanks ?

2 Answers

Jay McGavren
STAFF
Jay McGavren
Treehouse Teacher

Maximiliane Quel this is great! This already goes way beyond the parameters of the extra credit. I like the use of arrays, and the fact that it can accept any length of array, not just 4. And using inject is super-advanced! That's one I don't use very often myself.

There are always improvements to be made, though. Ready? Some of this will be advanced. Look up what you can, ask me questions about what you can't.

One improvement is to use the =~ operator with your regular expression, which will make it so you no longer need to call match. This will work just like your existing numeric? method:

def numeric? test
  test =~ /\d/
end

Another improvement is to use the printf function to round the output the a reasonable number of decimal places.

printf("You entered the numbers %s. The average is %0.2f.\n", x, average)

Regular expressions in general are slow, so it's better to avoid matching when possible. If to_f returns a number other than 0, we can be certain the input was numeric (or that it started with a number, at least). If to_f returns 0, the input could be non-numeric. Or, it could be that the user input was 0 or 0.0. If we do a regular expression test on the input only when the result is 0, we'll do the matching much less often, which is more efficient.

Returning nil makes it clear that convert_to_f was unsuccessful. nil is treated as false, a sign we should break out of our loop.

def user_answer
  print "Please enter a number (or press another key to finish): "
  gets.chomp
end

def convert_to_f(input)
  # Attempt to convert string to a Float.
  float = input.to_f
  # If we got 0, and the input was not "0" or "0.0", return nil.
  if float == 0 && input !~ /\A0\.?0?\Z/
    return nil
  # Otherwise, return whatever number we got from to_f.
  else
    return float
  end
end


def create_arr
  arr = []

  loop do
    input = user_answer
    # Attempt to convert the input to a Float.
    number = convert_to_f(input)
    # If nil was returned, it wasn't a number, so exit the loop.
    break unless number
    # Otherwise, appent the number to the array and continue.
    arr = arr.push number
  end

  return arr
end

x = create_arr

sum = x.inject 0, &:+
average = sum / x.length

printf("You entered the numbers %s. The average is %0.2f.\n", x, average)

You probably have questions. :) Ask away!

Maximiliane Quel
Maximiliane Quel
Courses Plus Student 55,489 Points

That's exactly what I was looking for. Thanks very much. It helps seeing how someone else would solve it and getting this kind of feedback. I found the most challenging part was finding a way let people finish inputting numbers and your solution with the break condition is great. Really appreciated.

I also didn't know I could improve performance by avoiding tests for regular expressions. And I hardly ever used printf but will probably use it in the future.

This isn't even my question, but thank you both very much. Way over my head at the moment, but I look forward to coming back and understanding all of this. Meanwhile, I was able to complete the extra credit as well, but nowhere near as elegantly.

# My approach to the extra credit was likely more complicated than it needed to be.
# Apologies in advance for the newbie code. :)

puts "*** Welcome to ~Averages~, where everyone can be anyone. ***"

print "Please enter the amount of numbers you will be averaging: "
numbers = gets().to_i
counter = numbers

print "Okay. You may enter the first number: "
x = gets().chomp

total = x.to_f

  while counter > 1
    print "And the next? "
    y = gets().to_f
    total += y
    counter -= 1
  end

average = total / numbers

puts "\nYou entered #{numbers} numbers...\n...the sum of which is #{total.to_i.round(2)}..."
puts "...the average of which is #{average.round(2)}."
puts "\nHave an average day..."


# Here is sample output:
#
# *** Welcome to ~Averages~, where everyone can be anyone. ***
# Please enter the amount of numbers you will be averaging: 3 
# Okay. You may enter the first number: 90
# And the next? 87
# And the next? 98
#          
# You entered 3 numbers...          
# ...the sum of which is 275.0...
# ...the average of which is 91.67.            
#          
# Have an average day...     

So...yeah. I was just glad it worked, but thanks, @Maximiliane Quel, for giving me something to aspire to! Thanks as well to Jay McGavren for taking it up another notch. I had a feeling you were keeping things deceptively simple, and to be honest was feeling a bit bored. Glad to know I have much, much to learn.

Cheers!