Gryphon59
Gryphon59

Reputation: 3

Ruby Iterating through 2D arrays and Populating with random data

I have to generate a dynamically sized 2D array with a predetermined value in the first index of each sub array, three random values in each of the three following indices (each falling in a different range), and finally, a calculated total of the three random indices. Here is what I have so far.

Sample code

print("Please enter the number of athletes competing in the triathalon: ")
field=gets.to_i

    count=1
    athlete = Array.new(5)
    triathalon = Array.new(field){athlete}
    triathalon.each do
            athlete.each do
                    athlete.insert(0,count)
                    athlete.insert(1,rand(30..89))
                    athlete.insert(2,rand(90..119))
                    athlete.insert(3,rand(120..360))
            #calculate total time per athlete
                    athlete.insert(4,athlete[1]+athlete[2]+athlete[3])
                    count+=1
            end
    end

Upvotes: 0

Views: 486

Answers (1)

iGian
iGian

Reputation: 11183

One possible option is using Range and mapping the range using Enumerable#map.

For example given n = 3 athletes, basic example:

(1..n).map { |n| [n] } #=> [[1], [2], [3]]

So, adding some of your specifications to the basic example:

n = 3
res = (1..n).map do |n|
    r1 = rand(30..89)
    r2 = rand(90..119)
    r3 = rand(120..360)
    score = r1 + r2 + r3
    [n, r1, r2, r3, score]
end

#=> [[1, 38, 93, 318, 449], [2, 64, 93, 259, 416], [3, 83, 93, 343, 519]]


An alternative way of pushing the sum of element into the array is using Object#tap:

[5,10,15].tap{ |a| a << a.sum } #=> [5, 10, 15, 30]

So you could write:

[rand(30..89), rand(90..119), rand(120..360)].tap{ |a| a << a.sum }

This allows to write a one liner (using Array#unshift):

(1..n).map { |n| [rand(30..89), rand(90..119), rand(120..360)].tap{ |a| a << a.sum }.unshift n }


Fixing your code

Visualise the setup:

field = 3 # no user input for example
p athlete = Array.new(5) #=> [nil, nil, nil, nil, nil]
p triathalon = Array.new(field){athlete.dup} #=> [[nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil], [nil, nil, nil, nil, nil]]

NOTE athlete.dup to avoid reference to the same object.

Once you see your objects (athlete and triathalon), you can realize that it is not required to iterate over the nested array, just access by index:

count=1
triathalon.each do |athlete|
    athlete[0] = count
    athlete[1] = rand(30..89)
    athlete[2] = rand(90..119)
    athlete[3] = rand(120..360)
    athlete[4] = athlete[1] + athlete[2] + athlete[3]
    count+=1
end

Improvement: to get rid of the counter use Enumerable#each_with_index.

Upvotes: 2

Related Questions