Error with an array of strings

T

Tom Smith

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

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.
 
T

Tim Hunter

Tom said:
Hi,
This is clearly a Ruby beginner question, so please be nice :)

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.

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]
 
T

Tom Smith

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

undefined method `/' for [["10", "20"]]:Array (NoMethodError)

TIA
 
7

7stud --

Tom Smith wrote:

1)
$ 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.


2)
number = "12"
number.to_i
puts number.class

--output:--
String


3) 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)
 
7

7stud --

Tim said:
Tom said:
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.
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

#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
 
T

Tom Smith

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"
 
T

Tim Hunter

Tom said:
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"

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.
 
D

Dylan

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"

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
 
B

Brian Candler

7stud said:
$ 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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,780
Messages
2,569,609
Members
45,254
Latest member
Top Crypto TwitterChannel

Latest Threads

Top