Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically Create Arrays in Ruby

Tags:

arrays

ruby

hash

Is there a way to dynamically create arrays in Ruby? For example, let's say I wanted to loop through an array of books as input by a user:

books = gets.chomp

The user inputs:

"The Great Gatsby, Crime and Punishment, Dracula, Fahrenheit 451,
Pride and Prejudice, Sense and Sensibility, Slaughterhouse-Five, 
The Adventures of Huckleberry Finn"

I turn this into an array:

books_array = books.split(", ")

Now, for each book the user input, I'd like to Ruby to create an array. Pseudo-code to do that:

 x = 0

 books_array.count.times do
    x += 1
    puts "Please input weekly sales of #{books_array[x]} separated by a comma."
    weekly_sales = gets.chomp.split(",")
 end

Obviously this doesn't work. It would just re-define weekly_sales over and over again. Is there a way to achieve what I'm after, and with each loop of the .times method create a new array?

like image 845
michaelmichael Avatar asked Dec 01 '09 21:12

michaelmichael


4 Answers

weekly_sales = {}
puts 'Please enter a list of books'
book_list = gets.chomp
books = book_list.split(',')

books.each do |book|
  puts "Please input weekly sales of #{book} separated by a comma."
  weekly_sales[book] = gets.chomp.split(',') 
end

In ruby, there is a concept of a hash, which is a key/value pair. In this case, weekly_sales is the hash, we are using the book name as the key, and the array as the value.

A small change I made to your code is instead of doing books.count.times to define the loop and then dereference array elements with the counter, each is a much nicer way to iterate through a collection.

like image 89
Matt Briggs Avatar answered Oct 02 '22 03:10

Matt Briggs


The "push" command will append items to the end of an array.

Ruby Docs->Array->push

like image 32
Derek Avatar answered Oct 02 '22 04:10

Derek


result = "The Great Gatsby, Crime and Punishment, Dracula, Fahrenheit 451,
  Pride and Prejudice, Sense and Sensibility, Slaughterhouse-Five, 
  The Adventures of Huckleberry Finn".split(/,\s*/).map do |b|
    puts "Please input weekly sales of #{b} separated by a comma."
    gets.chomp.split(',') # .map { |e| e.to_i }
end
p result

Remove the comment if you would like the input strings converted to numbers

like image 40
DigitalRoss Avatar answered Oct 02 '22 05:10

DigitalRoss


One way or another you need a more powerful data structure.

Your post gravitates toward the idea that weekly_sales would be an array paralleling the books array. The drawback of this approach is that you have to maintain the parallelism of these two arrays yourself.

A somewhat better solution is to use the book title as a key to hash of arrays, as several answers have suggested. For example: weekly_sales['Fahrenheit 451'] would hold an array of sales data for that book. This approach hinges on the uniqueness of the book titles and has other drawbacks.

A more robust approach, which you might want to consider, is to bundle together each book's info into one package.

At the simplest end of the spectrum would be a list of hashes. Each book would be a self-contained unit along these lines:

books = [
    {
        'title' => 'Fahrenheit 451',
        'sales' => [1,2,3],
    },
    {
        'title' => 'Slaughterhouse-Five',
        'sales' => [123,456],
    },
]

puts books[1]['title']

At the other end of the spectrum would be to create a proper Book class.

And an intermediate approach would be to use a Struct (or an OpenStruct), which occupies a middle ground between hashes and full-blown objects. For example:

# Define the attributes that a Book will have.
Book = Struct.new(:title, :weekly_sales)
books = []

# Simulate some user input.
books_raw_input = "Fahrenheit 451,Slaughterhouse-Five\n"
sales_raw_input = ['1,2,3', '44,55,66,77']
books_raw_input.chomp.split(',').each do |t|
    ws = sales_raw_input.shift.split(",")
    # Create a new Book.
    books.push Book.new(t, ws)        
end

# Now each book is a handy bundle of information.
books.each do |b|
    puts b.title
    puts b.weekly_sales.join(', ')
end
like image 24
FMc Avatar answered Oct 02 '22 05:10

FMc