7

I want a twodimensional array in Ruby, that I can access for example like this:

if @array[x][y] == "1" then @array[x][y] = "0"

The problem is: I don't know the initial sizes of the array dimensions and i grow the array (with the << operator).

How do I declare it as an instance variable, so I get no error like this?

undefined method `[]' for nil:NilClass (NoMethodError)

QUESTION UPDATED:

@array = Array.new {Array.new} 

now works for me, so the comment from Matt below is correct!

I just found out the reason why I received the error was because I iterated over the array like this:

for i in 0..@array.length
    for j in 0..@array[0].length
        @array[i][j] ...

which was obviously wrong and produced the error. It has to be like this:

for i in 0..@array.length-1
        for j in 0..@array[0].length-1
            @array[i][j] ...
kadrian
  • 3,911
  • 8
  • 32
  • 60
  • 6
    Ruby uses dynamic typing -- so you don't need to "declare" that a variable is a two-dimensional array up front. What you can do is create a variable and initialize it to an array. Then start putting arrays inside of it. – Matt Fenwick Feb 04 '12 at 21:51
  • for array = Array.new {Array.new} need to be changed to array = Array.new(3) {Array.new(2)},need have number inside – asdjkag Aug 28 '14 at 03:13

3 Answers3

7

A simple implementation for a sparse 2-dimensional array using nested Hashes,

class SparseArray
  attr_reader :hash

  def initialize
    @hash = {}
  end

  def [](key)
    hash[key] ||= {}
  end

  def rows
    hash.length   
  end

  alias_method :length, :rows
end

Usage:

sparse_array = SparseArray.new
sparse_array[1][2] = 3
sparse_array[1][2] #=> 3

p sparse_array.hash
#=> {1=>{2=>3}}

#
# dimensions
#
sparse_array.length    #=> 1
sparse_array.rows      #=> 1

sparse_array[0].length #=> 0
sparse_array[1].length #=> 1
csherin
  • 358
  • 1
  • 6
  • 1
    You can avoid the `rescue` by providing a block to `fetch`: `hash.fetch(key){ hash[key] = {} }` will be much faster. Also, `hash[key] ||= {}` is shorter and more commonly used. – Marc-André Lafortune Feb 04 '12 at 22:54
  • @Marc-AndréLafortune: Noted :-). The fetch and rescue was unnecessary. – csherin Feb 04 '12 at 23:06
  • is it possible to have a .length method for each of the dimensions? – kadrian Feb 05 '12 at 09:54
  • @ka2011r: Yes, see the updated code. Additionally you could also mixin Enumerable module and override #each to get more functionality. – csherin Feb 05 '12 at 10:40
5

Matt's comment on your question is totally correct. However, based on the fact that you've tagged this "conways-game-of-life", it looks like you are trying to initialize a two dimensional array and then use this in calculations for the game. If you wanted to do this in Ruby, one way to do this would be:

a = Array.new(my_x_size) { |i| Array.new(my_y_size) { |i| 0 }}

which will create a my_x_size * my_y_size array filled with zeros.

What this code does is to create a new Array of your x size, then initialize each element of that array to be another Array of your y size, and initialize each element of each second array with 0's.

Marc Talbot
  • 2,031
  • 2
  • 22
  • 26
3

Ruby's Array doesn't give you this functionality.

Either you manually do it:

(@array[x] ||= [])[y] = 42

Or you use hashes:

@hash = Hash.new{|h, k| h[k] = []}
@hash[42][3] = 42
@hash # => {42 => [nil, nil, nil, 42]}
Marc-André Lafortune
  • 72,927
  • 15
  • 152
  • 163
  • 1
    I ran with your idea of using Hashes to write SparseArray. There is also an example implementation using Hashes in the Ruby way book. – csherin Feb 04 '12 at 22:28