Error with an array of strings

Hi,
This is clearly a Ruby beginner question, so please be nice :slight_smile:

This program is supposed to take user input of numbers and average the
numbers. I figured I’d create a loop, an array, and then average the
numbers by dividing by the total length of the array.

#Get numbers
total = [ ]

until $_ == “n”
puts “Enter Number:\n”
number = STDIN.gets
number.chop!
number.to_i

total.push(number) #puts all the numbers into the array

print “add more numbers to average? y or n\n”
continue = STDIN.gets
continue.chop!

end

#Compute Average

all = total.slice(0…total.length) #slices the array no matter how large
it is

averageall = [all.slice(0…total.length)] / total.length

puts “Average is:”, averageall,"\n"

Needless to say, I’m getting an error with the averageall, since (I
assume) it’s trying to divide by strings. I figure I need to convert the
array of strings to an array of integers. Alas, I can’t seem to track
down the correct solution. Any help would be much appreciated for this
newbie to Ruby.

Thanks.

btw, the error is (as you might have guessed):

undefined method `/’ for [[“10”, “20”]]:Array (NoMethodError)

TIA

Needless to say, I’m getting an error with the averageall, since (I
assume) it’s trying to divide by strings. I figure I need to convert the
array of strings to an array of integers. Alas, I can’t seem to track
down the correct solution. Any help would be much appreciated for this
newbie to Ruby.

Thanks.

Tom S. wrote:

until $_ == “n”

Needless to say, I’m getting an error with the averageall, since (I
assume) it’s trying to divide by strings. I figure I need to convert the
array of strings to an array of integers. Alas, I can’t seem to track
down the correct solution. Any help would be much appreciated for this
newbie to Ruby.

Thanks.

The to_i method converts a string to an integer, assuming the string
really can be so converted.

“1”.to_i => 1

[“1”, “2”, “3”].map {|d| d.to_i} => [1, 2, 3]

Tom S. wrote:

$ ri Kernel#gets

------------------------------------------------------------ Kernel#gets
gets(separator=$/) => string or nil

 Returns (and assigns to +$_+) the next line from the list of files
 in +ARGV+ (or +$*+), or from standard input if no files are present
 on the command line.

So you can just use gets instead of STDIN.gets.

number = “12”
number.to_i
puts number.class

–output:–
String

  1. This does nothing:

total = [1, 2, 3]
all = total.slice(0…total.length)
p total

–output:–
[1, 2, 3]

Needless to say, I’m getting an error with the averageall, since (I
assume) it’s trying to divide by strings.

No, you are getting an error because you are trying to divide an array
by an integer. What do you expect the result to be?

I figure I need to convert the
array of strings to an array of integers.

puts [1, 2, 3] / 10

–output:–
undefined method `/’ for [1, 2, 3]:Array (NoMethodError)

Tim H. wrote:

Tom S. wrote:

until $_ == “n”

Needless to say, I’m getting an error with the averageall, since (I
assume) it’s trying to divide by strings. I figure I need to convert the
array of strings to an array of integers. Alas, I can’t seem to track
down the correct solution. Any help would be much appreciated for this
newbie to Ruby.

Thanks.

The to_i method converts a string to an integer, assuming the string
really can be so converted.

“1”.to_i => 1

[“1”, “2”, “3”].map {|d| d.to_i} => [1, 2, 3]

No.

total.push(number.to_i)

The op’s atrocious choice of variable names is coming back to haunt him.
The op is treating total as if it is an Integer. However, total is an
array. Just because you call something a dog doesn’t mean it isn’t a
pig. That reminds me of the question:

How many legs does a dog have if you call its tail a leg?

A: Four. Just because you call a dog’s tail a leg doesn’t mean it’s a
leg.

#Get numbers
total = [ ]

until $_ == “n”
puts “Enter Number:\n”
number = STDIN.gets
number.chop!
number.to_i #this is returning a new int, not converting number to
int
#you would need to do number = number.to_i
#of course, you can do to_f to get a Float

total.push(number) #puts all the numbers into the array

print “add more numbers to average? y or n\n”
continue = STDIN.gets
continue.chop!
end

#Compute Average

#multiply by 1.0 to make it a Float (integer division will truncate
remainders)
#inject just passes some value through each of the indexes
#in this case we are passing the sum, it’s initial value is zero
#so we pass zero as the argument, then in the block, we take
#the sum and the number. whatever the block returns will be
#plugged in as the sum in the next iteration, and whatever the last
#iteration returns will be what the method returns. This is why we do
#sum+num.to_f instead of sum += num.to_f

averageall = 1.0 * total.inject(0){|sum,num| sum + num.to_f } /
total.length

print “Average is: “, averageall,”\n” #you’re getting puts and print
confused

END
#The below code might be a suitable replacement for your input
#though 7stud had a good point, calling that variable “total” is not
wise.
#You could actually add the value to total, and keep another variable
#to track how many inputs there were. That would actually be more
efficient
#and remove the need for the inject method.

total = []
loop do
print "Enter Number: "
total << gets.to_f #get the number as a
float,
push it onto total
print "Do you have more numbers to average? "
break if gets =~ /^n/i #get the input, continue
if
the first letter isn’t n
end

p total #inspect the input

Thanks all for your help.

Josh, that’s definitely an improved loop, thanks. In any case, here’s
the improved code I’ve ended up with (pre-changing the loop):

all_inputs = Array.new

until $_ == “n”
puts “Enter Number:\n”
response = gets
response.chop!
response.to_f

all_inputs.push(response)

print “add more numbers to average? y or n\n”
continue = gets
continue.chop!

end

averageall = all_inputs.inject(0){|sum,num| sum + num.to_f } /
all_inputs.length

print “Average is: “, averageall,”\n”

On Aug 28, 2:56 pm, Tom S. [email protected] wrote:

response.chop!
averageall = all_inputs.inject(0){|sum,num| sum + num.to_f } /
all_inputs.length

print “Average is: “, averageall,”\n”

Posted viahttp://www.ruby-forum.com/.

Looks like you’re going through a lot of extra work for something
relatively simple. You require 4 keystrokes minimum for every number.

def getAvg
arr = []
number = 0
print “Enter Number (leave blank to finish): "
until number == “”
number=gets.chop!
arr << number.to_i if(number!=”")
end
sum=0.0
arr.each{|e| sum += e}
return sum/arr.length
end

irb(main):65:0> getAvg
Enter Number (leave blank to finish): 10
5

=> 7.5
irb(main):66:0> getAvg
Enter Number (leave blank to finish): 4
12
67

=> 27.6666666666667

Or you could make the user do the work for you:

def getAvg
print “Enter Numbers (separated by commas): "
input=gets.chop!
input=input.split(”,")
sum=0.0
input.each{|e| sum += e.to_i}
return sum/input.length
end

irb(main):065:0> getAvg
Enter Numbers (separated by commas): 5,5,5,5,5,10,10,10,10,10
=> 7.5
irb(main):066:0> getAvg
Enter Numbers (separated by commas): 1,2,3,4,5
=> 3.0
irb(main):067:0> getAvg
Enter Numbers (separated by commas): 1,2,3,4,20
=> 6.0
irb(main):068:0> getAvg
Enter Numbers (separated by commas): 1,2,3,4,5,24
=> 6.5

Tom S. wrote:

response.chop!
averageall = all_inputs.inject(0){|sum,num| sum + num.to_f } /
all_inputs.length

print “Average is: “, averageall,”\n”

Note that response.to_f doesn’t actually do anything. The to_f method
returns a number, it doesn’t change the value in response, so all you’re
doing it converting the string to a Float and then discarding it.
Instead, what you should do is something like this:

all_inputs.push(response.to_f)

Since the array values are already floats, you won’t have to convert the
array values to numbers in the inject block.

7stud – wrote:

$ ri Kernel#gets

------------------------------------------------------------ Kernel#gets
gets(separator=$/) => string or nil

 Returns (and assigns to +$_+) the next line from the list of files
 in +ARGV+ (or +$*+), or from standard input if no files are present
 on the command line.

So you can just use gets instead of STDIN.gets.

But only if you never put any arguments on the command line. STDIN.gets
is safer for exactly this reason, when you actually want to read from
STDIN.