Note to self: Array.to_a returns self
-
Since one should not iterate the collection one modify, such as looping over entities and removing some, I've always used
.to_a
- which works well when you have aSketchup::Entities
object.But I ran into a gotcha today: when the collection of entities is an Array.
entities_array.to_a
=> reference to the same array. I'd always assumed it'd return a copy...I got several methods that accepts
Sketchup::Entities
andArray
that I need to rewrite now to ensure I get a copy of the collection, regardless of type of object.This public gotch-ha announcement has been brought to you by Not-Enough-Sleep Inc. Stay tuned for the next episode!
-
a1=[1,2,3]
both
a2=a1 ### makes a new reference to the same array, 'a1' a2=a1.to_a ### makes 'a1' into an array [which it is already!] and then makes a new reference to the same array, 'a1' !!
will still refer toa1
, so consequently changinga2
changesa1
too !
BUT
a2=a1.dup
makes a separate copy ofa1
, as will
a2=a1+[]
as adding two arrays together produces a new array, even when the second one is empty;
and now changinga2
leavesa1
alone -
For clarity, Thom's Thread title states that array.dup returns the same array, but TIG says that array.dup returns a new array. Which is it actually (so I don't have to take the extra 10 seconds to test it
).
-
@chris fullmer said:
For clarity, Thom's Thread title states that array.dup returns the same array, but TIG says that array.dup returns a new array. Which is it actually (so I don't have to take the extra 10 seconds to test it
).
Wooops - that was a confused brain connection on my part. .to_a was what I really meant. I figued you guys would just read my brain to what I really meant.
-
@tig said:
a1=[1,2,3]
both
a2=a1 ### makes a new reference to the same array, 'a1' a2=a1.to_a ### makes 'a1' into an array [which it is already!] and then makes a new reference to the same array, 'a1' !!
will still refer toa1
, so consequently changinga2
changesa1
too !
BUT
a2=a1.dup
makes a separate copy ofa1
, as will
a2=a1+[]
as adding two arrays together produces a new array, even when the second one is empty;
and now changinga2
leavesa1
aloneTo clarify, since "assignment creates references"...:
a1=[1,2,3]
### creates a new array object referenced bya1
a2=a1
### makes a new reference (a2
) to the same array object thata1
is pointing at.
a2=a1.to_a
###a1.to_a
returns*self*
both references are pointing at the same array object, so consequently that object can be changed using either referencea2
ora1
.
BUT
a2=a1.dup
### makes a new array object, (referenced bya2
,) that is a copy of the array object thata1
is pointing at. -
The Ruby RDoc says:
Array#to_a
||Array#to_ary
Returns*self*
. If called on a subclass ofArray
, converts the receiver to anArray
object.
Object#clone
Produces a shallow copy of*self*
— the instance variables of*self*
are copied, but not the objects they reference. Copies the frozen and tainted state of*self*
. See also the discussion underObject#dup
.
Object#dup
Produces a shallow copy of*self*
— the instance variables of*self*
are copied, but not the objects they reference.dup
copies the tainted state of*self*
. See also the discussion underObject#clone
. In general,clone
anddup
may have different semantics in descendent classes. Whileclone
is used to duplicate an object, including its internal state,dup
typically uses the class of the descendent object to create the new instance.This method may have class-specific behavior. If so, that behavior will be documented under the
#initialize_copy
method of the class.
Note: I changed the words "obj" to "
self
" fo clarity.
The old "Pick-Axe" book Programming Ruby, says:
What happens when you copy a frozen object? That depends on the method you use. If you call an object's
clone
method, the entire object state (including whether it is frozen) is copied to the new object. On the other hand,dup
typically copies only the object's contents---the new copy will not inherit the frozen status.and also:
<span class="syntaxdefault">person1 </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"Tim"</span><span class="syntaxdefault"> <br />person2 </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> person1 <br />person1</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">0</span><span class="syntaxkeyword">]</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">'J'</span><span class="syntaxdefault"> <br />person1 » </span><span class="syntaxstring">"Jim"</span><span class="syntaxdefault"> <br />person2 » </span><span class="syntaxstring">"Jim"</span><span class="syntaxdefault"> <br /> </span>
Assignment aliases objects, potentially giving you multiple variables that reference the same object. But can't this cause problems in your code? It can, but not as often as you'd think (objects in Java, for example, work exactly the same way). For instance, in the example in Figure 3.1, you could avoid aliasing by using the
dup
method of String, which creates a new String object with identical contents.<span class="syntaxdefault">person1 </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"Tim"</span><span class="syntaxdefault"> <br />person2 </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> person1</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">dup <br />person1</span><span class="syntaxkeyword">[</span><span class="syntaxdefault">0</span><span class="syntaxkeyword">]</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> </span><span class="syntaxstring">"J"</span><span class="syntaxdefault"> <br />person1 » </span><span class="syntaxstring">"Jim"</span><span class="syntaxdefault"> <br />person2 » </span><span class="syntaxstring">"Tim"</span><span class="syntaxdefault"> <br /> </span>
-
@dan rathbun said:
@tig said:
a1=[1,2,3]
both
a2=a1 ### makes a new reference to the same array, 'a1' a2=a1.to_a ### makes 'a1' into an array [which it is already!] and then makes a new reference to the same array, 'a1' !!
will still refer toa1
, so consequently changinga2
changesa1
too !
BUT
a2=a1.dup
makes a separate copy ofa1
, as will
a2=a1+[]
as adding two arrays together produces a new array, even when the second one is empty;
and now changinga2
leavesa1
aloneTo clarify, since "assignment creates references"...:
a1=[1,2,3]
### creates a new array object referenced bya1
a2=a1
### makes a new reference (a2
) to the same array object thata1
is pointing at.
a2=a1.to_a
###a1.to_a
returns*self*
both references are pointing at the same array object, so consequently that object can be changed using either referencea2
ora1
.
BUT
a2=a1.dup
### makes a new array object, (referenced bya2
,) that is a copy of the array object thata1
is pointing at.Nice round-up..
But be aware #dup performs a shallow copy. So any proper objects (as opposed to simple numbers) in that array are copied by reference.
So:
a = [10, 20, [30,31,32]]
c = a.dup
a[2][0] = 'Tada!'
c
will print:
[10, 20, ["Tada!", 31, 32]]
Advertisement