Abraham P
Abraham P

Reputation: 15471

Why does array concatenation lead to (infinitely?) nested array?

If the following code is run in an IRB console:

arr = [[], 1]
arr[0]<<arr

The result is [[[...], 1]].

Thereafter arr[0], arr[0][0], arr[0][0][0], etc. produce the same output.

This sort of makes sense, because I can't display arbitrary amounts of nesting right?

However, arr[0][1] produces nil (rather then 1) what? But arr[0][0][1] prints 1.

arr[0][0][0][1] => nil

then:

arr[0][0][0][0][1] => 1

etc.

What is going on? What is the structure that is actually created? How deep does this structure recurse? Where are the nils coming from?

Upvotes: 0

Views: 57

Answers (2)

Arup Rakshit
Arup Rakshit

Reputation: 118271

Array#size is the essential too to demonstrate why sometimes you are seeing nil or 1 as the output:

arr = [[], 1]
arr[0]<< arr
arr.size # => 2
arr # => [[[...]], 1]

arr[0] # => [[[...], 1]]
arr[0].size # => 1
arr[0][1] # => nil

arr[0][0] # => [[[...]], 1]
arr[0][0].size # => 2
arr[0][0][1] # => 1

arr[0][0][0] # => [[[...], 1]]
arr[0][0][0].size # => 1
arr[0][0][0][1] # => nil

arr[0][0][0][0] # => [[[...]], 1]
arr[0][0][0][0].size # => 2
arr[0][0][0][0][1] # => 1

Upvotes: 1

oldergod
oldergod

Reputation: 15010

Just see what is inside

arr[0]
# => [[[...], 1]]

arr[0] is an array that has only one element. So [1] will be out of this array and return nil.

arr[0][0]
# => [[[...]], 1]

arr[0][0] will get into the only one element of the top array and this element contains two values so [1] will found a value.

It behaves so because you did arr[0] << arr and not arr[0] = arr.
By doing arr = [[], 1]; arr[0] << arr you did not set arr as arr[0] but your added arr as an element of arr[0] which is an array itself.

If you had done otherwise

arr = ["whatever", 1]
# => ["whatever", 1]
arr[0] = arr
# => [[...], 1]
arr[0][1]
# => 1
arr[0][0][1]

Edit

Back to your question, why does it become a recursive array. You are not passing arr's value to arr[0] but its reference.

arr = ["whatever", 1]
# => ["whatever", 1]
arr.object_id
# => 69999412942060
arr[0] = arr
# => [[...], 1]
arr[0].object_id
# => 69999412942060
arr[0].object_id == arr.object_id
# => true

You have Ouroboros right there since arr and arr[0] refers the same object.
If you wanted to only insert its value you should have deep_copied it first.

arr = ["whatever", 1]
arr_copy = Marshal.load(Marshal.dump(arr)) # copy the value,
                                           # delete the object reference dependence
arr[0] = arr_copy
puts arr.inspect
# [["whatever", 1], 1] # no infinit recurrence.

Upvotes: 1

Related Questions