# Use loop counter for 2D array index

Noob: I’m trying to fill a 2D array using a loop counter as the index, but I can’t figure out the finer syntax.

The code is along the lines of:

``````data = Array[[],[]]
counter_max = 750

for counter in 0..counter_max
itemX = #calculation X
itemY = #calculation Y
itemZ = #calculation Z

data[counter][0] = itemX
data[counter][1] = itemY
data[counter][2] = itemZ
end
``````

When I run this, I get the error “undefined method ‘=’ for nil:NilClass>”

If I replace the ‘[counter]’ index reference with [0] then I don’t get an error message. Also, if I use the same logic, but with several 1D arrays it works; so I know my basic syntax is right. I just can’t figure out why using the ‘counter’ variable causes an error with a 2D array.

Any help would be appreciated.

Thanks!

Not sure if I understand what you are trying to achieve but it seems that you have the index of the array in the assignment inverted. This works:

``````data = Array[[], [], []] # Were you missing an index?
counter_max = 10

for counter in 0..counter_max
itemX = counter / 2 # Any operation
itemY = counter * 2 # Any operation
itemZ = counter ** 2 # Or maybe you didn't need the third index?

data[0][counter] = itemX
data[1][counter] = itemY
data[2][counter] = itemZ
end
``````

More compact:

``````newdata = Array[[], [], []]
newdata[0] = (0..counter_max).map{|c| c / 2}
newdata[1] = (0..counter_max).map{|c| c * 2}
newdata[2] = (0..counter_max).map{|c| c ** 2}
``````

Why not use a single array, to contain an array like a ‘point’ ?
Perhaps easier to understand split up like this…

``````data=[]
max = 10
0..max{|c|
arr=[]
arr << c/2.0   ### x
arr << c*2.0   ### y
arr << c**2.0  ### z
data << arr
}
``````

Note how I’ve used a float for the math - an integer produces only integer results 1/2 >>> 0 but 1/2 >>> 0.5

Now because it’s a three element array `data[i].x` returns the X [or .y for Y or ,z for Z] value for index ‘i’…

1 Like

Nice. Didn’t now that the methods `.x`, `.y` and `.z` could be used for normal arrays as well. Very handy, thanks!

Based on the way the array `data` was originally defined I was assuming that he needed list of coordinates and not list of ‘points’, but this is probably not the case.

Those operations were just examples. After several years working with numerical methods, I am well aware of the issues with floating point arithmetic

The additional methods .x, .y and .z are added to Array by SketchUp, because a three-element Array can be used instead of a Point3d in many cases, and the matching methods also exist in that too…

Thanks for your answers guys. As I’ve only been teaching myself Ruby for less than a week, I don’t know what it all means…but I’ll get there! To further flesh out what I’m trying to achieve, I’ve attached a .jpg and the .rb file that drew it.
su_bentspring.rb (1.3 KB)

1 Like

Well, you could get ten Ruby coders together, and they’d all code a problem in a different way. But since you mentioned that you’re just starting, I made a few changes and added another loop to create a ‘tube’ with faces.

A few things –

• when making alot of changes to a model, wrap the operations with `start_operation` & `commit_operation` statements, which places a single entry in the ‘Edit - Undo’ list.

• Not sure why you were storing the points for the spring. They aren’t really needed, just the last point, which the ‘spring’ operation in my code shows.

• Ruby has many ways to run loops, I used an `upto` loop for my code.

• I changed the calcs a bit for fewer ops.

Nice code for the spring. Now lets see it using transforms, so it can be started and stopped anywhere in space. Just kidding…

bent_spring_tube.rb (2.3 KB)

Greg

Thanks for your answer Greg - always insightful to see how other coders do things. Thanks for the tips you offer too - they are handy things to know.

As far as the faces are concerned, I was more interested in a spring-like form than a tube form - but that’s ok. I’ve found the “Face.followme” method, so I’ll look into that.

Thanks once again for your help.

The code, `data = Array[[],[]]`
… actually creates an array with only two starting members, that are empty array objects.

This means they have members [0] and [1], and everything was fine the first two times through your loop.

The third time through, however when you were calling `data[2]` the `Array#[]` method returned `nil` because an index beyond the member range was passed. And the second subscript (index) call of `[0]=` is really a call of `nil[0]=`, which causes the `NoMethodError`, because the `NilClass` does not have a method named “`[]=`”.

It’s a quirk of Ruby that `nil` gets returned in cases like this, because index methods double as existence query methods. It’s as if you are asking first IF member `2` of `data` exits, return it otherwise return `nil`. In Ruby it is customary to test return values for “nilness” or boolean `true`, as `nil` gets logically evaluated as FALSE. (You will see this also in IO methods, where '`nil` is returned if the user cancels, or a file object does not exist.)
So after calling such methods, you do a boolean test before using the return value…

``````value = data[i]
if value
# use value
else
# recover gracefully, exit method, etc.
end
``````

You can initialize an array with n number of nested empty arrays thus:

``````data = Array::new(n) {|i| Array::new }
``````

(In this case, the index `i` is unused inside the block.)

Second your original loop would have had 751 iterations. You do not really have to use a max counter like that.

In Ruby can just do:

``````for counter in data_array
# code
end
``````

… and it will only iterate what members actually exist in the array.
It is similar to

``````data_array.each do |counter|
# code
end
``````

The `for` loop is slightly faster, as it does not create a new variable scope with every iteration. The `each` method does create a new scope.

As a newb, you may not realize that the `Enumerable` mixin module, is mixed into the `Array` and `Hash` classes, (as well as most of the SketchUp API’s collection classes.) This mixin library adds many useful iteration and query methods.

Good primer docs are listed here:
Index of Files, Classes & Methods in Ruby 2.7.2 (Ruby 2.7.2)
You can ignore the few at the top, beginning with “.lib” and “.test”.
Read the one on globals, then skip all the Rake docs.
Start again with the “re.rdoc” and down to the end of the list.

3 Likes

Good stuff Dan (you actually answered my original query - thanks for that!)

This has all been very informative. Feeling quite inadequate atm, but that’s normal when learning something new.