Recently I discovered that tap
can be used in order to "drily" assign values to new variables; for example, for creating and filling an array, like this:
array = [].tap { |ary| ary << 5 if something }
This code will push 5
into array
if something
is truthy; otherwise, array
will remain empty.
But I don't understand why after executing this code:
array = [].tap { |ary| ary += [5] if something }
array
remains empty. Can anyone help me?
In the first case array
and ary
point to the same object. You then mutate that object using the <<
method. The object that both array
and ary
point to is now changed.
In the second case array
and ary
again both point to the same array. You now reassign the ary
variable, so that ary
now points to a new array. Reassigning ary
however has no effect on array
. In ruby reassigning a variable never effects other variables, even if they pointed to the same object before the reassignment.
In other words array
is still empty for the same reason that x
won't be 42 in the following example:
x = 23
y = x
y = 42 # Changes y, but not x
Edit: To append one array to another in-place you can use the concat
method, which should also be faster than using +=
.
I want to expand on this a bit:
array = [].tap { |ary| ary << 5 if something }
What this does (assuming something
is true-ish):
assigns array
to []
, an empty array.
array.object_id = 2152428060
passes []
to the block as ary
. ary
and array
are pointing to the same array object.
array.object_id = 2152428060
ary.object_id = 2152428060
ary << 5
<< is a mutative method, meaning it will modify the receiving object. It is similar to the idiom of appending !
to a method call, meaning "modify this in place!", like in .map
vs .map!
(though the bang does not hold any intrinsic meaning on its own in a method name). ary
has 5 inserted, so ary
= array
= [5]
array.object_id = 2152428060
ary.object_id = 2152428060
We end with array
being equal to [5]
In the second example:
array = [].tap{ |ary| ary += [5] if something }
ary += 5
+=
is short for ary = ary + 5
, so it is first modification (+
) and then assignment (=
), in that order. It gives the appearance of modifying an object in place, but it actually does not. It creates an entirely new object.
array.object_id = 2152428060
ary.object_id = 2152322420
So we end with array
as the original object, an empty array with object_id=2152428060
, and ary
, an array with one item containing 5 with object_id = 2152322420
. Nothing happens to ary
after this. It is uninvolved with the original assignment of array
, that has already happened. Tap executes the block after array
has been assigned.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With