Last element of the ruby array is nil - ruby

I have a simple ruby program that has 2 steps so far
1. Ask the user for the number of stock market symbols they want to track
2. Ask the user to input these symbols
puts("How many stocks do you want to track ?")
numStocks = gets.chomp()
puts("Please enter #{numStocks} stock symbols: ")
array = Array.new(numStocks.to_i)
for i in 1..numStocks.to_i do
array.insert(i-1, gets.chomp())
end
puts("Stock symbols entered ... #{array}")
The output that is printed onto the console is
Stock symbols entered ... ["aapl", nil]
Why is the last element of the array nil in this case and what's the proper way to get rid of it ?

Array.new creates a new array, filling it with the quantity of elements you specified. Array.new(3) is the same as [nil, nil, nil]. Array.new(2, 'a') is the same as ['a', 'a'].
You then use array.insert which adds instead of replaces the elements. You could use array[i-1] = gets.chomp() to set the values, but there's really no reason to initialize the array this way at all.
A "more Ruby" way to write this all would be:
puts 'How many stocks do you want to track ?'
num_stocks = gets.chomp
puts "Please enter #{num_stocks} stock symbols: "
array = 1.upto(num_stocks.to_i).map do
gets.chomp
end
puts "Stock symbols entered ... #{array}"
EDIT:
Also, it’s worth mentioning that in Ruby, arrays are not a fixed size. You can add and remove elements from them as you please. If I had to guess, you’re used to languages like C, where you have to define the size of your array up front and then it’s just that size forever (that’s what I’m guessing you were trying to do anyways).
And another thing, in Ruby it’s not very common to use Array.new. Most times people just define an array by typing an array literal.
array = [1,2,3]
A ruby array is more like a List in other languages. It supports push (though << is a more common method for adding to an array), pop, and other list-like features.

Thats because when you do Array.new(numStocks.to_i) it initializes an array with 3 nil values and you keep adding on to it,
the proper way to get rid of nil from array is to use .compact on the array but I suggest you change your logic,
maybe something like this
puts("How many stocks do you want to track ?")
numStocks = gets.chomp()
puts("Please enter #{numStocks} stock symbols: ")
array = Array.new() # or array = []
numStocks.to_i.times do
array << gets.chomp()
end
puts("Stock symbols entered ... #{array}")
or you could ask the user to enter the comma separated symbols, so you don't have to loop, and split them,
puts("Please enter #{numStocks} stock symbols separated by commas (a,b): ")
symbols = gets.chomp().split(',')
puts("Stock symbols entered ... #{array}")

Related

Summing all numbers in an array

I want to put a possibly infinite amount of numbers in and then it's added to an array, which is then all added together.
I saw this on a few other questions but they were all just puts-ing the array, not summing.
case input
when 'add'
puts "Enter the numbers to add on separate lines then hit enter on another line"
add_array = []
numbers_to_add = " "
while numbers_to_add != ""
numbers_to_add = gets.chomp
add_array.push numbers_to_add
end
add_array.delete('')
add_array.map(&:to_f).inject(:+)
puts add_array
end
You can utilize the inject method.
[1,2,3].inject(:+) #=> 6
By the looks of your code I'd guess that your incoming array is an array of strings, not an array of numbers. To convert them to decimals (floats) you can use:
sum = add_array.map(&:to_f).inject(:+)
puts sum
This applies the #to_f operation on every element, then passes that to the summing function (#inject(:+))

Ensure all values in user-input array are between 1 and 10

I am trying to define an array based on user input and want to make sure that each value is between 1 and 10. How can I do that?
So I am using this right now.
array = gets.chomp
I want the user to only input values ranging from 1 to 10. How do I do that?
You can just use the below to do that:
def verify(input)
(1..10).to_a.include?(input)
end
#input is user input
if verify(input)
#do some stuff
else
#don't do stuff
end
...So you know, gets.chomp doesn't return an array. It returns a String. Something like this will give you the array you want:
array = gets.chomp.split(/\D/).map { |e| e.to_i }
That converts it from a single string (which happens to contain ,-separated values) to an array of numbers.
puts 'The array you entered was invalid!' if array.any? { |item| !(1..10).include?(item) }
That goes through and checks if any of the values return true for !(1..10).include?(item), which returns true if and only if the range [1,10] (inclusive) contains item. If so, it prints out The array you entered was invalid!.
However, it looks like what you want to do is physically prevent the user from entering a number like 11 or 12 into the console, which (in pure Ruby at least) is impossible. The closest you can get is validating the input after the fact, which is what this does. Look into Ruby's various loops if you want to have them keep entering the array until the array they enter is valid.
Note that #locoboy's answer will work for single numbers, but it will fail when attempting to validate the entire array, or the direct result of gets.chomp.

Max value of nested array in hash

What I have:
hash = {id =>[string, number], id =>[string, number]}
I need to get the max value of number. I have been able to do this, but when I puts it.
I get:
id
string
number
What I need please
id string number
This is what I've tried:
This brings the max value to the top of list, but I need to exclude the rest of the list.
hash.each{|x, y| puts "#{x} #{y[0]} #{y[1]}"}.max
This returns the max value but displays it vertically
puts hash.max_by{|y| "#{y}"}
I have tried numerous other things and am having a hard time wrapping my head around this one.
Not sure if it matters but I am read this in from a file into a hash, the number is a float
The max here doesn’t do anything (since it is called on hash and its return value never used):
hash.each{|x, y| puts "#{x} #{y[0]} #{y[1]}"}.max
This is the same as doing puts on an array (since that’s what max_by returns), which prints each element on a separate line. You’re also unnecessarily converting your number to a string (which can result in unexpected comparison results):
puts hash.max_by{|y| "#{y}"}
Instead let’s just get the max key/value pair:
max = hash.max_by { |id, (string, number)| number }
#=> ["the-id", ["the-string", 3.14]]
Now we can flatten and join the array before puts-ing it:
puts max.flatten.join(' ')
# prints: the-id the-string 3.14
I would re-arrange the hash with number as the key, then use sort_by(): http://www.rubyinside.com/how-to/ruby-sort-hash

Ruby "gets" prompts user for input. How do I get input (string) to reference existing object?

I'm prompting the user with gets to give me either peg1, peg2, or peg3, which each reference an array already made before the prompt. However, the input from the user is a string, which would be "peg1", "peg2", or "peg3". How do I make the user's input actually reference/attach to my 3 arrays that are already made?
If you assign all the possible arrays to a Hash keyed by the name of the array, you can simply ask the user for that name and then select the array from the hash. Using this technique, you don;t need to hardcode your values into a long case statement or (even worse) eval anything.
def ask_for_peg(pegs)
peg = nil
while peg.nil?
print("Which peg do you want? ")
id = gets.strip
peg = pegs[id]
end
peg
end
available_pegs = {
"peg1" => array_for_peg1,
"peg2" => array_for_peg2,
"peg3" => array_for_peg3
}
selected_peg = ask_for_peg(available_pegs)
# results in one of the arrays assigned in the available_pegs array above
It's hard to understand what you're asking, but taking some guesses at what you mean, I think something like this shows you how to do what you want:
$peg1 = [:peg, :one]
$peg2 = [:peg, :two]
$peg3 = [:peg, :three]
def ask_which_peg
print "Please choose peg1, peg2, or peg3: "
case gets.chomp
when "peg1"
$peg1
when "peg2"
$peg2
when "peg3"
$peg3
else
nil
end
end
peg = nil
until(peg)
peg = ask_which_peg()
end
print peg, "\n"
The name of the array is for your benefit, but cannot be used in the actual program. So you cannot check if the input equals the name of a variable. However, you can easily check if the input equals "peg1", "peg2", or "peg3" using an if statement if input = peg1 and return the proper array in each case.
This will maybe do what you are asking.
peg1 = ['yellow']
peg2 = ['blue']
peg3 = ['green']
input = gets.chomp
input =~ /peg\d/ and
puts eval("#{input}")
This is sufficiently edited (from prior answer) to avoid eval of user input as a Ruby command. Will raise error on peg4 or other peg that doesn't exist.
The and flow control helps to check inputs.

Can't convert String onto integer (TypeError)

Following code return error "Can't convert String onto integer", please help
subject = ['eng','Math','Sci']
grade = ['grade 1','grade 2','grade 3']
subject.each do |sub|
puts ("some string")
grade[sub] .each do |grd|
puts ("some string")
end
end
grade[sub] .each do |grd| thats the problem.
Array elements are accessed by using a index of integer or a range of integers.
You are trying to access a array element by using the variable stored in sub. Since this is a ordinary .each loop it will loop all the elements in the array, in this case 'eng','Math','Sci'. If you want the position of for example 'eng' you could use a .each_with_index
it should probably just be
grade.each do |grd|
with each_with_index it would be
subject.each_with_index do |sub, index|
print sub
print grade[index]
end
If you want a subject -> grade collection it might be good to look into using a Hash like Dave Newton said.
{"eng" => "grade 1","Math" => "grade 2","Sci" => "grade 3"}.each do |subject, grade|
puts "#{subject| #{grade}"
end
When you do
grade[sub] .each do |grd|
Ruby expects sub to be using an integer to reference a position in the list of grades.
subject and grade are both arrays. They can only be accessed by their position. In your each loop, the block gets the actual element (in sub), not the position. So in line 5, you are trying to access grade['eng'], which will not work and produces the error. Note that unlike in e.g. PHP, an array and a hash (an associative array) are different things.
Guessing from your code, you might want to use each_index instead of each which will pass the index number to the block instead of the element.
I'm not sure I understand what you're trying to achieve; however, if you'd like to print subjects and grades and you're sure about the relative order of elements in the arrays, you could do this:
subject = ['eng','Math','Sci']
grade = ['grade 1','grade 2','grade 3']
subject.each_with_index do |sub, idx|
puts "#{sub} - #{grade[idx]}"
end
Output:
eng - grade 1
math - grade 2
sci - grade 3
An hash is however probably more suitable to your needs.

Resources