CodeDependency
CodeDependency

Reputation: 135

Easiest way to populate an array with user input/ with the least amount of lines of code

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

Answers (5)

Jörg W Mittag
Jörg W Mittag

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

Cary Swoveland
Cary Swoveland

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

iGian
iGian

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

3limin4t0r
3limin4t0r

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

Amadan
Amadan

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

Related Questions