Nooby question : multidimensional arrays

Well am I right, that in Ruby there are only one dimensional arrays, and
that i have to add an array into an array to get multidimensional
arrays, or is there a simpler more ruby like way to create them ?

On Aug 16, 2006, at 2:37 AM, [email protected] wrote:

Well am I right, that in Ruby there are only one dimensional
arrays, and that i have to add an array into an array to get
multidimensional arrays, or is there a simpler more ruby like way
to create them ?

Yep. You create them by just making arrays of arrays and you
reference them just like C arrays
irb(main):001:0> ary = []
=> []
irb(main):002:0> ary << [1, 2, 3] << %w{dog cat bird} << [8, 10, 17]
=> [[1, 2, 3], [“dog”, “cat”, “bird”], [8, 10, 17]]
irb(main):003:0> ary[1][2]
=> “bird”


The folly of mistaking a paradox for a discovery, a metaphor for a
proof, a torrent of verbiage for a spring of capital truths, and
oneself for an oracle, is inborn in us.
-Paul Valery, poet and philosopher (1871-1945)

[email protected][email protected] writes:

Well am I right, that in Ruby there are only one dimensional arrays,
and that i have to add an array into an array to get
multidimensional arrays, or is there a simpler more ruby like way to
create them ?

Well, there’s also the block that Array.new takes to give its initial
value:

a = Array.new(8) {Array.new(8) {0}}

This creates an 8 by 8 two-dimensional array initialized with all 0.

(Technically, you don’t need that second block and could do the inner
bit as Array.new(8,0))

One would have to think in Ruby. For a C programmer, it can be a hard
transition but once one gets used to the Ruby way, you will find the C
way
bit inflexible and rigid. For example, in C, you might write:

#define NUM_COLS 20
#define NUM_ROWS 10
int ary[NUM_ROWS][NUM_COLS];

int i, j;
for (i = 0; i < NUM_ROWS; i++) {
for (j = 0; i < NUM_COLS; j++) {
ary[i][j] = some_code…
}
}

In Ruby, you don’t need to worry about setting up iterator variables
since
arrays know how to iterate themselves, e.g.

ary.each {|r|
r.each {|c|

}
}

If you need to access a specific element, you can still use the C-like
notation, i.e. ary[r][c].

----- Original Message -----
From: “Chris G.” [email protected]

On Wed, 2006-08-16 at 18:37 +0900, [email protected] wrote:

Well am I right, that in Ruby there are only one dimensional arrays, and that i have to add an array into an array to get multidimensional arrays, or is there a simpler more ruby like way to create them ?

Or, if you need a speed burst, NArray [http://narray.rubyforge.org/]
could help. Never used it myself yet though.

David V.

On Aug 16, 2006, at 4:37 AM, [email protected] wrote:

Well am I right, that in Ruby there are only one dimensional
arrays, and that i have to add an array into an array to get
multidimensional arrays, or is there a simpler more ruby like way
to create them ?

Others have given you great answers, but it’s also worth pointing out
that you can make a multidimensional Array, if you like:

class Array2D
def initialize(width, height)
@data = Array.new(width) { Array.new(height) }
end
def [](x, y)
@data[x][y]
end
def []=(x, y, value)
@data[x][y] = value
end
end
=> nil

arr = Array2D.new(3, 2)
=> #<Array2D:0x31d0b0 @data=[[nil, nil], [nil, nil], [nil, nil]]>

arr[1, 1] = “Hello”
=> “Hello”

arr
=> #<Array2D:0x31d0b0 @data=[[nil, nil], [nil, “Hello”], [nil, nil]]>

arr[1, 1]
=> “Hello”

James Edward G. II

01 class Array2D
02
03 def initialize(width, height)
04 @data = Array.new(width) { Array.new(height) }
05 end
06
07 def [](x, y)
08 @data[x][y]
09 end
10
11 def []=(x, y, value)
12 @data[x][y] = value
13 end
14
15 end

Can someone explain what’s going on in this code for a newbie.

I’m getting lost in the looping logic of the second def; and the 3rd def
I’m guessing has something do do with hashes?

I’m using this code in a simple 2d array. It was working but then I
changed all of my ranges from (0…value) to (1…value) [I used ranges to populate my 2D array with hashes] I now get the error message:

11:in []=': undefined method[]=’ for nil:NilClass (NoMethodError)

The first method simply creates an Array of size width and places an
Array of size height in each index, so

Array.new(2){Array.new(3)}
=> [ [ nil , nil , nil ] , [ nil , nil , nil] ]

The second method is merely a way of accessing a coordinate within the
2d array, so for
a = Array2D.new 2, 2
a[0,1]

it would first find @data[0] which would return the object in index 0
which is an array [ nil, nil ] and then @data[0][1] would return the
object in index 1 of the sub-array which in this case is nil. In fact
the logic here is no more complex than the logic for a simple Array…
it only looks more complex, because we are using the same method
twice… perhaps it’d help to think about it like this

def [](x, y)
sub_array = @data[x]
return sub_array[y]
end

The third def is exactly the same as the second def, except instead of
simply looking to see what’s stored at a coordinate, you’re changing
the object that’s stored at that coordinate. Again the logic is the
same as with simple arrays, except the object in each index is also an
array.

I don’t know exactly what’s causing the error you’re seeing, but it
looks like you’re calling [x,y]= using a value of x that is higher
than the maximum index in the outer array, so instead of returning a
sub_array it returns nil, and then tries to call [y]= on nil…

hth…

On Sep 12, 2009, at 00:06, Shawn W_ wrote:

04 @data = Array.new(width) { Array.new(height) }

Create an Array named @data initially with ‘width’ rows. Set the
value of each element of the array to a new array with ‘height’ rows.

If ‘width’ is 3 and ‘height’ is 2, @data initially looks like:

[[nil, nil], [nil, nil], [nil, nil]]

07 def [](x, y)
08 @data[x][y]
09 end

Create a method to access the elements like so:

obj[2,0] returns the value of @data[2][0] (here it would be nil)

11 def []=(x, y, value)
12 @data[x][y] = value
13 end

Create a method to set the values:

obj[1,1] = 4 assigns 5 to the array element at of @data[1][1]

After which, @data would look like:

[[nil, nil], [nil, 5], [nil, nil]]

I’m not sure what looping logic you’re talking about. There are no
loops here, nor any hashes. Array access syntax looks the same as
hashes. If you think there are hashes because of the block braces
{ }, it’s an unfortunate thing that they share the same syntax, but
generally whether it’s a block or an array is pretty clear from the
context.

Basically all that’s going on is that this class is creating a way of
doing 2d arrays with a subscript that looks like [x, y], and hiding
the fact that the underlying implementation is actually a 1
dimensional array containing at each element another one-dimensional
array.

I’m using this code in a simple 2d array. It was working but then I
changed all of my ranges from (0…value) to (1…value) [I used ranges to populate my 2D array with hashes] I now get the error message:

What’s probably happening is that you’re going beyond the initial size
of the array (width, height). If your array is 2 by 3 and you try to
do:

x[10, 20] = 5

Internally it tries to do @data[10][20] = 5. @data[10] is past the
end of the array, so its value is nil, so @data[10][20] = 5 becomes
nil[20] = 5.

Giving the error message:

11:in []=': undefined method[]=’ for nil:NilClass (NoMethodError)

You could probably fix that by changing the []= method to something
like:

def []=(x, y, value)

If @data[x] is nil, create it as an empty array

@data[x] ||= Array.new
@data[x][y] = value
end

If you do that, you should also fix [] to create missing values too:

def [](x, y)

If @data[x] is nil, create it as an empty array

@data[x] ||= Array.new
@data[x][y]
end

Ben

Ben G. wrote:

On Sep 12, 2009, at 00:06, Shawn W_ wrote:

04 @data = Array.new(width) { Array.new(height) }

Create an Array named @data initially with ‘width’ rows. Set the
value of each element of the array to a new array with ‘height’ rows.

If ‘width’ is 3 and ‘height’ is 2, @data initially looks like:

[[nil, nil], [nil, nil], [nil, nil]]

I have no problem understanding this bit. An instance variable has been
created called “data”. The value of this variable is unique for each
object created in this class. This variable is equal to an array inside
an array; the {} are used to pass the Array.new(height) into each part
of the Array.new(width).

07 def [](x, y)
08 @data[x][y]
09 end

Create a method to access the elements like so:

obj[2,0] returns the value of @data[2][0] (here it would be nil)

I understand what this is doing but not how it works. As far as I can
see it’s creating a class Array2D method named [], with parameters x and
y. So when you call this method it should look like:

2D_array_obj = Array2D.new(3,8)
puts 2D_array_obj. <-- should put whatever is in row 1, column 1.
puts 2D_array_obj[1,1] <-- but this is what works to display row 1,
column 1.

How can you name a method [] when such syntax is used so often
elsewhere? How do those 1’s get inside the square brackets?

Then there is the line:

08 @data[x][y]
Which is actually:

08 a 2D array [x][y]
Is the [][] syntax a way of accessing an array inside an array normally?
I know you can access a hash inside array using this method. I am even
using such code to substitute values into a hash inside my Array2D
object like so:
2D_array_obj[x,y][“hash_key”] = new value

11 def []=(x, y, value)
12 @data[x][y] = value
13 end

Create a method to set the values:

obj[1,1] = 4 assigns 5 to the array element at of @data[1][1]

After which, @data would look like:

[[nil, nil], [nil, 5], [nil, nil]]

I assume “obj[1,1] = 4 assigns 5…” is a typo and should be “obj[1,1] =
5 assigns 5…”?

Here we have a different syntax with []= being used. Is []= the name of
the method? Now you’ve explained it I know what this is doing but it’s
still not clear how it works.

I’m not sure what looping logic you’re talking about. There are no
loops here, nor any hashes. Array access syntax looks the same as
hashes. If you think there are hashes because of the block braces
{ }, it’s an unfortunate thing that they share the same syntax, but
generally whether it’s a block or an array is pretty clear from the
context.

I thought because of the [][] that it was accessing a hash. Isn’t this
how you access a hash inside an array?

I’m using this code in a simple 2d array. It was working but then I
changed all of my ranges from (0…value) to (1…value) [I used ranges to populate my 2D array with hashes] I now get the error message:

What’s probably happening is that you’re going beyond the initial size
of the array (width, height). If your array is 2 by 3 and you try to
do:

x[10, 20] = 5

Internally it tries to do @data[10][20] = 5. @data[10] is past the
end of the array, so its value is nil, so @data[10][20] = 5 becomes
nil[20] = 5.

Giving the error message:

11:in []=': undefined method[]=’ for nil:NilClass (NoMethodError)

You could probably fix that by changing the []= method to something
like:

def []=(x, y, value)

If @data[x] is nil, create it as an empty array

@data[x] ||= Array.new
@data[x][y] = value
end

If you do that, you should also fix [] to create missing values too:

def [](x, y)

If @data[x] is nil, create it as an empty array

@data[x] ||= Array.new
@data[x][y]
end

I think I know what’s going wrong. The size of the array hasn’t changed,
but where I start entering values into the array has, which means
there’s no room for the last row and column. Basically what I’ve done is
change (0…5), which is 0 1 2 3 4, to (1…5), which is 1 2 3 4 5 - both
the same size. But when I use this new range to iterate through my array
the first position addressed is now 1; so I’m trying to fit 5 units into
a 5 unit box without filling up the space in the box allocated to unit
1.

A better way to put it:

was:
[0,1,2,3,4]

now:
[nil,1,2,3,4]5

Hi –

On Sat, 12 Sep 2009, Shawn W_ wrote:

puts 2D_array_obj[1,1] <-- but this is what works to display row 1,
column 1.

How can you name a method [] when such syntax is used so often
elsewhere? How do those 1’s get inside the square brackets?

You can name it [] because the parser knows what it means in a given
context. This:

identifier[]

always means the [] method.

As for the 1’s inside the brackets: that’s syntactic sugar. When Ruby
sees this:

x[y]

it automatically converts it to:

x.

Then there is the line:

08 @data[x][y]
Which is actually:

08 a 2D array [x][y]
Is the [][] syntax a way of accessing an array inside an array normally?

It really means this:

@data..

In other words, it’s calling the [] method first on @data, then on the
return value of @data[x].

Create a method to set the values:
Here we have a different syntax with []= being used. Is []= the name of
the method? Now you’ve explained it I know what this is doing but it’s
still not clear how it works.

Yes, []= is the name of the method.

I thought because of the [][] that it was accessing a hash. Isn’t this
how you access a hash inside an array?

It’s all about method calls. [][] by itself doesn’t mean array or hash
or any other class of object. If you do this:

x[y][z]

then you’re calling the [] method on x, with the argument y – which
can return an array, a hash, a string, a Person instance, anything at
all – and then you’re calling [] on whatever you got back, with the
argument z.

David

Is this an okay job of implementing a multidimensional array?

Thank you for any feedback.

-Kedar

uhm…i’m not sure what your trying to do with this, but what if i
wanted to
get the 5th element in the 4th dimension?

i’d write the generator method something like this:

while i < @n
j = 0
while j < @m
@array[[i]] << 0
j += 1
end
i += 1
end

(i havnt tested it, so it may not work)

then you’d need to define the other accessor methods & such… but
thats a
start

hex

Thanks for the feedback!

I simply thought it would be nice if you can address elements simply as:
A[i, j, k …]

uhm…i’m not sure what your trying to do with this, but what if i
wanted to
get the 5th element in the 4th dimension?

Wouldn’t that simply be dc[0,0,0,5]?

i’d write the generator method something like this:

while i < @n
j = 0
while j < @m
@array[[i]] << 0
j += 1
end
i += 1
end

(i havnt tested it, so it may not work)

then you’d need to define the other accessor methods & such… but
thats a
start

hex

alright, so i’ve played around with it a bit more, and i kind of like
your
implementation… though if your trying to do what i think you are there
are
a few bugs in the implementation you should fix.

from what i can see you want someone to be able to use a call like:
dc[0,1]
and get the second element of the second dimension of the data cube
right?
i think it’s kinda cool, but the way it’s currently implemented calling
dc[3] does the same thing. in fact, check out this little irb session i
just did:

irb(main):100:0> dc = DataCube.new(1, 1)
=> [0]
irb(main):101:0> dc[1,1]=1
=> 1
irb(main):102:0> dc
=> [0, nil, nil, 1]
irb(main):103:0> dc[1,0]=2
=> 2
irb(main):104:0> dc
=> [0, 2, nil, 1]
irb(main):105:0> dc[3]=7
=> 7
irb(main):106:0> dc
=> [0, 2, nil, 7]

i dont think that’s the behavior you want. that aside, if you can get
it
working the way you want it to i think it’d be a pretty cool way to
access
everything.

though how would i get all the elements in dimension 3? reading up on
data
cubes real quick one may want to do that… but overall it looks good
and
your implementation - though to my best knowledge unorthodox - will
probably
work fine if done right.

hex

(oh, and pizza rox sox!!)

Thanks for the feedback!

np

I simply thought it would be nice if you can address elements simply as:
A[i, j, k …]

true, it’d be nice but it kinda wont work… (see next)

uhm…i’m not sure what your trying to do with this, but what if i
wanted to
get the 5th element in the 4th dimension?

Wouldn’t that simply be dc[0,0,0,5]?

no, that wont work. try your code in irb, with your 3x5 data cube…

irb(main):055:0* dc = DataCube.new(3, 5)
=> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
0, 0]
irb(main):056:0> dc[0,1,2]=44
=> 44
irb(main):057:0> puts dc[0, 1, 2]
44
=> nil
irb(main):058:0> puts dc.size
125
=> nil
irb(main):059:0> dc
=> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44,
0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
0]

thats the actual representation of the data cube in your computer. i’m
not
sure that’s exactly what you want, but it dosnt really look right to me.

i dunno, mabye you are right and this is just a funky way to do this,
i’ll
take a longer look a little later, but now PIZZA!!

hex

(yes, pizza is more important than you :P)

On 01/07/11 17:56, Kedar M. wrote:

Is this an okay job of implementing a multidimensional array?

https://github.com/kedarmhaswade/datacube/blob/master/data_cube.rb

Thank you for any feedback.

-Kedar

There’s a book:

An APL Compiler

which describes an expansion vector which is a scan of
the array sizes. IOW, for:

arr = Array.new(s0,s1,…sn)

the expansion vector for this arr is:

arr.ev = [1, s0, s0s1, s0s1s3,…, s0s1*…*sn]

The i-th element of arr.ev is the distance between successive
elements in the i-th dimension. IOW:

the locatoin of:
arr[i0,i1,…, ij, … in]
is located arr.ev[j] elements from:
arr[i0,i1,…, ij+1, … in]

IOW:

arr[i0,i1,..., ij, ... in]

is located:

i0*arr.ev[0]+i1*arr.ev[1]+...+in*arr.ev[n]

elements from the 1st element:

arr[0,0,...,0]

Thus, given the sizes, you can create a member variable
which is the expansion vector, then use that to access the
elements by calculating the offset from the initial element
using the dot product:

i0*arr.ev[0]+i1*arr.ev[1]+...+in*arr.ev[n]

HTH.

Good luck.

On 01/07/11 21:57, Larry E. wrote:
[snip]

Thus, given the sizes, you can create a member variable
which is the expansion vector, then use that to access the
elements by calculating the offset from the initial element
using the dot product:

i0*arr.ev[0]+i1*arr.ev[1]+...+in*arr.ev[n]

Also, if you’re interested in transposing the vector,
IIRC, that just invovles permuting the expansion vector.
IOW, if the original expansion vector was:

evo = [e1,e2,e3,…,en-1,en]

then simply reversing it:

evt = [en,en-1,…,e3,e2,e1]

and using the same vector of values for elements,
would result in a transposed array. I guess you
could try that for just 2 dimensional array
to see if that works.

-Larry

On 01/07/11 21:57, Larry E. wrote:

The expansion vector for DataCube.new(n,m) would be:

[m0,m1,m2,…,mn]

IOW, the length of this expansion vector is n+1.

[snip]

Thus, given the sizes, you can create a member variable
which is the expansion vector, then use that to access the
elements by calculating the offset from the initial element
using the dot product:

i0*arr.ev[0]+i1*arr.ev[1]+...+in*arr.ev[n]

However, after looking at:

https://github.com/kedarmhaswade/datacube/blob/master/data_cube.rb#L48

it’s not clear that to_index calculates the same offset because
coefficients[i] corresponds to one of the i0,i1,…,in-1 in the
above formulat, and coefficients[i] is not multiplied by anything.
It’s only added to mega_indexn, and mega_indexn involves
multiplication by the dimension, n, instead of ths uniform size, m.

HTH

-Larry