5

As mentioned in this answer, Array.new(size, object) creates an array with size references to the same object.

hash = Hash.new
a = Array.new(2, hash)
a[0]['cat'] = 'feline'
a # => [{"cat"=>"feline"},{"cat"=>"feline"}]
a[1]['cat'] = 'Felix'
a # => [{"cat"=>"Felix"},{"cat"=>"Felix"}]

Why does Ruby do this, rather than doing a dup or clone of object?

3 Answers 3

9

Because that's what the documentation says it does. Note that Hash.new is only being called once, so of course there's only one Hash

If you want to create a new object for every element in the array, pass a block to the Array.new method, and that block will be called for each new element:

>> a = Array.new(2) { Hash.new }
=> [{}, {}]
>> a[0]['cat'] = 'feline'
=> "feline"
>> a
=> [{"cat"=>"feline"}, {}]
>> a[1]['cat'] = 'Felix'
=> "Felix"
>> a
=> [{"cat"=>"feline"}, {"cat"=>"Felix"}]
Sign up to request clarification or add additional context in comments.

8 Comments

"Because that's what the documentation says it does." - I was hoping to pop the why stack a little further than that.
Popping the why stack: because when you pass in an object to be used to pre-populate the array, the array will be pre-populated with that object.
@Justice: Were you being serious or sarcastic with that comment?
Both. Serious, because the second parameter to Array.new is indeed the parameter used to pre-populate the array. Sarcastic, because Gareth said as much in his answer, and I am simply restating what he said.
I would flip that around and say, why would you want Ruby to implicitly create copies of an arbitrary object (which might be an expensive operation), when you also have a mechanism to carry out the duplication yourself?
|
2

For certain classes that can't be modified in-place (like Fixnums) the Array.new(X, object) form works as expected and is probably more efficient (it just calls memfill instead of rb_ary_store and yielding to the block):

For more complicated objects you always have the block form (e.g. Array.new(5) { Hash.new }).

*Edit:* Modified according to the comments. Sorry for the stupid example, I was tired when I wrote that.

3 Comments

You're replacing c[1] with a new object, rather than modifying it.
>> c[1] << "h"; c #=> ["ah", "ah", "ah", "ah", "ah"]
Sorry, I wrote that answer right before going to bed, you are right of course. The rest should still be true too.
0

I came up with this answer, very short and simple solution

c_hash = Hash.new
["a","b","c","d","e","f"].each do |o|
  tmp = Hash.new
  [1,2,3].map {|r| tmp[r] = Array.new}
  c_hash[o] = tmp
end
c_hash['a'][1] << 10
p c_hash

3 Comments

But the question is why all indexes reference the same object
References in Ruby (and in almost every other programming language) point to a single object in memory. - stackoverflow.com/a/46330723/5793994 The reference never changed. The original reference is and will always in this case be the original r_hash. The reference was never moved away form that hash. This is just classic objects. :)
You didn't understand. Your answer should contain answer to the asked question. Question is why but you answer how to avoid it. Don't explain in comments, edit your answer

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.