53

Maybe it's just my lack of abilities to find stuff here that is the problem, but I can't find anything about how to create multidimensional arrays in Ruby.

Could someone please give me an example on how to do it?

10 Answers 10

45

Strictly speaking it is not possible to create multi dimensional arrays in Ruby. But it is possible to put an array in another array, which is almost the same as a multi dimensional array.

This is how you could create a 2D array in Ruby:

a = [[1,2,3], [4,5,6], [7,8,9]]


As stated in the comments, you could also use NArray which is a Ruby numerical array library:

require 'narray'
b = NArray[ [1,2,3], [4,5,6], [7,8,9] ]

Use a[i][j] to access the elements of the array. Basically a[i] returns the 'sub array' stored on position i of a and thus a[i][j] returns element number j from the array that is stored on position i.

Sign up to request clarification or add additional context in comments.

9 Comments

As an alternative (for scientific calculations/dense data), consider NArray or encoding method.
It's important to note the syntax for accessing this is a[1][2][1] and not a[1,2,1] as some might expect.
@veger how do i get the values from that array?
@andkjaer: already mentioned by tadman, but I extended my answer with some extra details on the workings of it.
This is extensible to multi dimensional arrays > dim 2. E.g. my3Darray=[[[1,2,3],[4,5,6]],[['a','b','c'],['d','e','fgh']]]; puts my3Darray[1][1][2][1]
|
23

you can pass a block to Array.new

Array.new(n) {Array.new(n,default_value)}

the value that returns the block will be the value of each index of the first array,

so..

Array.new(2) {Array.new(2,5)} #=> [[5,5],[5,5]]

and you can access this array using array[x][y]

also for second Array instantiation, you can pass a block as default value too. so

Array.new(2) { Array.new(3) { |index| index ** 2} } #=> [[0, 1, 4], [0, 1, 4]]

2 Comments

You don't need to specify the size of the arrays when initializing this way. myArray = Array.new{Array.new} works fine to initialize an empty array that's ready to have arrays pushed into it.
The example given by @emery is dangerous: one cannot index into such an array: (Array.new{Array.new})[2][2] = :value # => NoMethodError: undefined method '[]=' for nil:NilClass
11

Just a clarification:

arr = Array.new(2) {Array.new(2,5)} #=> [[5,5],[5,5]]

is not at all the same as:

arr = Array.new(2, Array.new(2, 5))

in the later case, try:

arr[0][0] = 99

and this is what you got:

[[99,5], [99,5]]

Comments

5

There are two ways to initialize multi array (size of 2). All the another answers show examples with a default value.

Declare each of sub-array (you can do it in a runtime):

multi = []
multi[0] = []
multi[1] = []

or declare size of a parent array when initializing:

multi = Array.new(2) { Array.new }

Usage example:

multi[0][0] = 'a'
multi[0][1] = 'b'
multi[1][0] = 'c'
multi[1][1] = 'd'

p multi # [["a", "b"], ["c", "d"]]
p multi[1][0] # "c"

So you can wrap the first way and use it like this:

@multi = []
def multi(x, y, value)
  @multi[x] ||= []
  @multi[x][y] = value
end

multi(0, 0, 'a')
multi(0, 1, 'b')
multi(1, 0, 'c')
multi(1, 1, 'd')

p @multi # [["a", "b"], ["c", "d"]]
p @multi[1][0] # "c"

Comments

3

The method given above don't works.

n = 10
arr = Array.new(n, Array.new(n, Array.new(n,0.0))) 
arr[0][1][2] += 1
puts arr[0][2][2]

is equivalent to

n = 10
a = Array.new(n,0.0)
b = Array.new(n,a)
arr = Array.new(n, b) 
arr[0][1][2] += 1
puts arr[0][2][2]

and will print 1.0, not 0.0, because we are modifiyng array a and printing the element of array a.

Comments

1

I had to reproduce PHP-style multidimensional array in Ruby recently. Here is what I did:

# Produce PHP-style multidimensional array.
#
# Example
#
# arr = Marray.new
#
# arr[1][2][3] = "foo"
# => "foo"
#
# arr[1][2][3]
# => "foo"

class Marray < Array
  def [](i)
    super.nil? ? self[i] = Marray.new : super
  end
end

Comments

0

Actually this is much quicker than the block method given above:

arr = Array.new(n, Array.new(n, Array.new(n,0.0))) 

arr[0][1][2] += 1

1 Comment

Example from @Kri-ban is incorrect. The nested arrays are not all new objects but shared across cells, e.g: a = Array.new(2, Array.new(2, 0.0)); a[0][1] = 5; a # => [[0.0, 5], [0.0, 5]] the 5 appears at indices [0][1] and [1][1] even though it was only set once.
0

Perhaps you can simulate your multidimensional Array with a Hash. The Hash-key can by any Ruby object, so you could also take an array.

Example:

marray = {}
p marray[[1,2]]   #-> nil
marray[[1,2]] = :a
p marray[[1,2]]   #-> :a

Based on this idea you could define a new class.

Just a quick scenario:

=begin rdoc
Define a multidimensional array.

The keys must be Fixnum.

The following features from Array are not supported:
* negative keys (Like Array[-1])
* No methods <<, each, ...
=end
class MArray
  INFINITY = Float::INFINITY
=begin rdoc
=end
  def initialize(dimensions=2, *limits)
    @dimensions = dimensions
    raise ArgumentError if limits.size > dimensions
    @limits = []
    0.upto(@dimensions-1){|i|
      @limits << (limits[i] || INFINITY)
    }
    @content = {}
  end
  attr_reader :dimensions
  attr_reader :limits
=begin rdoc
=end
  def checkkeys(keys)
    raise ArgumentError, "Additional key values for %i-dimensional Array" % @dimensions if keys.size > @dimensions
    raise ArgumentError, "Missing key values for %i-dimensional Array" % @dimensions if keys.size != @dimensions
    raise ArgumentError, "No keys given" if keys.size == 0
    keys.each_with_index{|key,i|
      raise ArgumentError, "Exceeded limit for %i dimension" % (i+1) if key > @limits[i]
      raise ArgumentError, "Only positive numbers allowed" if key < 1

    }
  end
  def[]=(*keys)
    data = keys.pop
    checkkeys(keys)
    @content[keys] = data
  end
  def[](*keys)
    checkkeys(keys)
    @content[keys]
  end
end

This can be used as:

arr = MArray.new()
arr[1,1] = 3
arr[2,2] = 3

If you need a predefined matrix 2x2 you can use it as:

arr = MArray.new(2,2,2)
arr[1,1] = 3
arr[2,2] = 3
#~ arr[3,2] = 3  #Exceeded limit for 1 dimension (ArgumentError)

I could imagine how to handle commands like << or each in a two-dimensional array, but not in multidimensional ones.

Comments

0

It might help to remember that the array is an object in ruby, and objects are not (by default) created simply by naming them or naming a the object reference. Here is a routine for creating a 3 dimension array and dumping it to the screen for verification:

def Create3DimensionArray(x, y, z, default)
    n = 0                       # verification code only
    ar = Array.new(x)
    for i in 0...x
        ar[i] = Array.new(y)
        for j in 0...y
            ar[i][j] = Array.new(z, default)
            for k in 0...z      # verification code only
                ar[i][j][k] = n # verification code only
                n += 1          # verification code only
            end                 # verification code only
        end
    end
    return ar
end

# Create sample and verify
ar = Create3DimensionArray(3, 7, 10, 0)

for x in ar
    puts "||"
    for y in x
        puts "|"
        for z in y
            printf "%d ", z
        end
    end
end

1 Comment

Better verification code: <pre>for x in 0...ar.length for y in 0...ar[x].length for z in 0...ar[x][y].length printf "%3d ", ar[x][y][z] end puts "|" end puts "--" end </pre>
0

Here is an implementation of a 3D array class in ruby, in this case the default value is 0

class Array3
 def initialize
   @store = [[[]]]
 end

 def [](a,b,c)
  if @store[a]==nil ||
    @store[a][b]==nil ||
    @store[a][b][c]==nil
   return 0
  else
   return @store[a][b][c]
  end
 end

 def []=(a,b,c,x)
  @store[a] = [[]] if @store[a]==nil
  @store[a][b] = [] if @store[a][b]==nil
  @store[a][b][c] = x
 end
end


array = Array3.new
array[1,2,3] = 4
puts array[1,2,3] # => 4
puts array[1,1,1] # => 0

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.