views:

65

answers:

4

Hi there,

Excuse the newbie question. I'm trying to create a two dimensional array in ruby, and initialise all its values to 1. My code is creating the two dimensional array just fine, but fails to modify any of its values.

Can anyone explain what I'm doing wrong?

  def mda(width,height)
     #make a two dimensional array
     a = Array.new(width)
     a.map! { Array.new(height) }

     #init all its values to 1
     a.each do |row|
       row.each do |column|
          column = 1
       end
     end
     return a
  end

Thanks!
Tristan

A: 

column in your nested each loop is a copy of the value at that place in the array, not a pointer/reference to it, so when you change its value you're only changing the value of the copy (which ceases to exist outside the block).

If you just want a two-dimensional array populated with 1s something as simple as this will work:

def mda(width,height)
  [ [1] * width ] * height
end

Pretty simple.


By the way, if you want to know how to modify the elements of a two-dimensional array as you're iterating over it, here's one way (starting from line 6 in your code):

 #init all its values to 1
 a.length.times do |i|
   a[i].length.times do |j|
     a[i][j] = 1
   end
 end
Jordan
See comments under Nakilon's answer: this array creation does not work (try to change one of it's values).
steenslag
+1  A: 

each passes into the block parameter the value of each element, not the element itself, so column = 1 doesn't actually modify the array.

You can do this in one step, though - see the API docs for details on the various forms of Array#new. Try a = Array.new(width) {|i| Array.new(height) {|j| 1 } }

Chris
No need to include block variables when they aren't being used.
Lars Haugseth
I did not know that. I'd assumed they were like method params, and Ruby would complain if they were absent. Ah well - YLSNED!
Chris
A: 

you can create it like this?

a=Array.new(width) { Array.new(height,1) }
ghostdog74
+2  A: 

It the line row.each do |column| the variable column is the copy of the value in row. You can't edit its value in such way. You must do:

def mda(width,height)
  a = Array.new(width)
  a.map! { Array.new(height) }
  a.each do |row|
    row.map!{1}
  end
  return a
end

Or better:

def mda(width,height)
  a = Array.new(width)
  a.map! { Array.new(height) }

  a.map do |row|
    row.map!{1}
  end
end

Or better:

def mda(width,height)
  a = Array.new(width){ Array.new(height) }
  a.map do |row|
    row.map!{1}
  end
end

Or better:

def mda(width,height)
  Array.new(width) { Array.new(height){1} }
end
Nakilon
Are 'width' and 'height' mixed up? Also, this can be done without blocks: Array.new(height, Array.new(width, 1))
steenslag
@steenslag, No, it can't. If you are building multi-dimensional array in such way, when you assign value to one element of it, it will be spread to the whole column. It would be like `array = [[1]*w]*h`, where you will just make `h` copies of the *pointer* to the same 1-dimensional array. About mixing of `width` and `height` — it is another question, which was not supposed to be discussed by author.
Nakilon
P.S.: see ghostdog74's answer. It must have at least one block.
Nakilon
Ah, yes. thank you.
steenslag