Reputation: 135
What is the easiest way of creating an array of x amount of elements comprised of mixed data types (ie, array with strings, ints and floats) from user input
So far, I made some code that worked using a for loop, but I wanted to know if there was a way to optimize it and have the least amount of lines of code.
puts "how many elements?"
max = gets.to_i
array = []
for i in 0..max - 1
puts "are you entering in a string, an int or a float?"
data_type = gets.chomp
if %W[string STRING String s S].include?(data_type)
puts "enter in a string"
array[i] = gets.chomp
elsif %W[int INT Int i I].include?(data_type)
puts "enter an int"
array[i] = gets.to_i
elsif %W[Float FLOAT float f F].include?(data_type)
puts "enter a float"
array[i] = gets.to_f
end
end
print array
Upvotes: 1
Views: 196
Reputation: 369458
So far, I made some code that worked using a for loop, but I wanted to know if there was a way to optimize it and have the least amount of lines of code.
In Ruby, newlines are never required, so "the least amount of lines of code" for any problem in Ruby is always 1:
puts "how many elements?"; max = gets.to_i; array = []; for i in 0..max - 1 do puts "are you entering in a string, an int or a float?"; data_type = gets.chomp; if %W[string STRING String s S].include?(data_type) then puts "enter in a string"; array[i] = gets.chomp elsif %W[int INT Int i I].include?(data_type) then puts "enter an int"; array[i] = gets.to_i elsif %W[Float FLOAT float f F].include?(data_type) then puts "enter a float"; array[i] = gets.to_f end end; print array
Upvotes: 0
Reputation: 110675
OK, I'll bite.
p Array.new(puts("How many elements?") || gets.to_i) {
puts("Are you entering in a string, an int or a float?") ||
case(gets.chomp)
when "string", "S" then (puts("Enter a string") || gets.chomp)
when "int", "INT" then (puts("Enter an integer") || gets.to_i)
when "float", "F" then (puts("Enter a float") || gets.to_f)
end
}
The following dialog:
How many elements?: 3
Are you entering in a string, an int or a float?: int
Enter an integer: 5
Are you entering in a string, an int or a float?: S
Enter a string: hi
Are you entering in a string, an int or a float?: F
Enter a float: 3.4
results in the following to be displayed (and returned):
[5, "hi", 3.4]
I used p
rather than puts
(which would display the elements of this array one per line) to make clear that it's an array that's being displayed. Note that each puts
in the snipped returns nil
, so nil || x #=> x
.
This snippet has eight lines, but it can be reduced to one by removing the newlines:
p Array.new(puts("How many elements?") || gets.to_i) { puts("Are you entering in a string, an int or a float?") || case(gets.chomp) when "string", "S" then (puts("Enter a string") || gets.chomp) when "int", "INT" then (puts("Enter an integer") || gets.to_i) when "float", "F" then (puts("Enter a float") || gets.to_f) end }
Upvotes: 2
Reputation: 11193
Using a Hash
as an helper:
datatypes = { i: { convert: 'to_i', text: 'an integer' }, f: { convert: 'to_f', text: 'a float' } }
datatypes.default = { convert: 'to_s', text: 'a string' }
array = max.times.map do
puts "Are you entering in an (i)nt, a (f)loat or a string (default)?"
data_type = gets[0].downcase.to_sym # you can improve this part
puts "enter in #{datatypes[data_type][:text]}"
gets.chomp.send(datatypes[data_type][:convert])
end
p array
p array.map &:class
Upvotes: 0
Reputation: 21130
Without changing any behaviour apart from accepting string
, int
and float
as case insensitive (eg. stRinG
now also works) you could do something like this.
puts "how many elements?"
max = gets.to_i
array = max.times.map do
puts "are you entering in a string, an int or a float?"
case gets
when /\A(string|s)\Z/i
puts "enter in a string"
gets.chomp
when /\A(int|i)\Z/i
puts "enter an int"
gets.to_i
when /\A(float|f)\Z/i
puts "enter a float"
gets.to_f
end
end
print array
Note: You might want to add an else inside the case-statement, to handle the scenario that the user doesn't enter string
, int
or float
. Currently It will result in a nil
value in the array.
You might want to implement this in the following manner:
case gets
when # ...
#...
else
redo
end
Upvotes: 2
Reputation: 198314
Minimum number of lines? One. Once you have max
:
array = max.times.map { gets.chomp.then { |l| case l when /^\d+$/ then l.to_i when /^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$/ then l.to_f else l end } }
This is even shorter (though some people will object):
array = max.times.map { gets.chomp.then { |l| Integer(l) rescue Float(l) rescue l } }
It would be more readable to write it in several lines though.
Note also that Rubyists basically pretend for
does not exist in the language, and will typically replace it with Enumerable#each
, Integer#times
and similar.
This is not quite the same as what you have; my code makes it impossible to have a string that would be a valid number, like e.g. "2.0"
. Your code is not too bad if you want that functionality (and obsession with number of lines is generally misguided). Things I would change:
Loop. array = max.times.map do ... end
over for
any time. (This also makes explicit assignment to array[i]
unnecessary.)
"float".start_with?(data_type.downcase)
instead of %W[Float FLOAT float f F].include?(data_type)
, so you don't need to worry about listing all the variations.
Upvotes: 5