# Suprised by destructive map (not map!)

**URL:** <https://www.ruby-forum.com/t/suprised-by-destructive-map-not-map/123016>\
**Category:** Ruby\
**Created:** [December 13, 2007, 1:17pm UTC](https://www.ruby-forum.com/t/suprised-by-destructive-map-not-map/123016 "2007-12-13T13:17:23Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![bor\_S](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/0ea827/32.png) [@bor\_S](https://www.ruby-forum.com/u/bor_S)\
**Post date:** [December 13, 2007, 1:17pm UTC](https://www.ruby-forum.com/t/suprised-by-destructive-map-not-map/123016/1 "2007-12-13T13:17:23Z")

</div>

Occasionally I run into bits of ruby that are surprising to me (although  
might not be to matz). Here is one of them. Should I file this as a bug  
(I thought .map is supposed to be non-destructive), or just keep in mind  
which classes are passed by value and which are passed by reference.

Basically: map (and naturally also map!) is destructive when your array  
includes elements which are normally not passed by value but by  
reference.

ruby 1.8.6 (2007-09-24 patchlevel 111) [x86\_64-linux]  
boris:falcon:~:irb

“unexpected (for me)”  
irb(main):001:0\> array = [[1],[2]]  
=\> [[1], [2]]  
irb(main):002:0\> array.map {|i| i \<\< 3}  
=\> [[1, 3], [2, 3]]  
irb(main):003:0\> array  
=\> [[1, 3], [2, 3]]  
irb(main):004:0\>

“expected behaviour”  
irb(main):005:0\* array = [1,2]  
=\> [1, 2]  
irb(main):006:0\> array.map {|i| i + 3}  
=\> [4, 5]  
irb(main):007:0\> array  
=\> [1, 2]  
irb(main):008:0\>

---

<div class="post-metadata">

**Author:** ![bor\_S](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/0ea827/32.png) [@bor\_S](https://www.ruby-forum.com/u/bor_S)\
**Post date:** [December 13, 2007, 1:52pm UTC](https://www.ruby-forum.com/t/suprised-by-destructive-map-not-map/123016/2 "2007-12-13T13:52:08Z")

</div>

Boris S. wrote:

> boris:falcon:~:irb
> 
> “unexpected (for me)”  
> irb(main):001:0\> array = [[1],[2]]  
> =\> [[1], [2]]  
> irb(main):002:0\> array.map {|i| i \<\< 3}  
> =\> [[1, 3], [2, 3]]  
> irb(main):003:0\> array  
> =\> [[1, 3], [2, 3]]  
> irb(main):004:0\>

It’s not map that’s destructive here, it’s the #\<\< method. Map iterates  
over the original array, and creates a new one with the results of the  
block - it doesn’t copy the original array first.

---

<div class="post-metadata">

**Author:** ![bor\_S](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/0ea827/32.png) [@bor\_S](https://www.ruby-forum.com/u/bor_S)\
**Post date:** [December 13, 2007, 2:00pm UTC](https://www.ruby-forum.com/t/suprised-by-destructive-map-not-map/123016/3 "2007-12-13T14:00:55Z")

</div>

Use it:  
irb(main):001:0\> array = [[1], [2]]  
=\> [[1], [2]]  
irb(main):002:0\> array.map{|a| a += [3]}  
=\> [[1, 3], [2, 3]]  
irb(main):003:0\> array  
=\> [[1], [2]]

---

<div class="post-metadata">

**Author:** ![bor\_S](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/0ea827/32.png) [@bor\_S](https://www.ruby-forum.com/u/bor_S)\
**Post date:** [December 13, 2007, 2:09pm UTC](https://www.ruby-forum.com/t/suprised-by-destructive-map-not-map/123016/4 "2007-12-13T14:09:09Z")

</div>

Bernardo Monteiro R. wrote:

> Use it:  
> irb(main):001:0\> array = [[1], [2]]  
> =\> [[1], [2]]  
> irb(main):002:0\> array.map{|a| a += [3]}

That expands to:

array.map{|a| a = a + [3] }

The Array#+ method isn’t destructive, and a variable assignment in a  
block isn’t either.

---

<div class="post-metadata">

**Author:** ![bor\_S](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/0ea827/32.png) [@bor\_S](https://www.ruby-forum.com/u/bor_S)\
**Post date:** [December 13, 2007, 4:19pm UTC](https://www.ruby-forum.com/t/suprised-by-destructive-map-not-map/123016/5 "2007-12-13T16:19:37Z")

</div>

Exactly

---

<div class="post-metadata">

**Author:** ![bor\_S](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/0ea827/32.png) [@bor\_S](https://www.ruby-forum.com/u/bor_S)\
**Post date:** [December 13, 2007, 7:28pm UTC](https://www.ruby-forum.com/t/suprised-by-destructive-map-not-map/123016/6 "2007-12-13T19:28:01Z")

</div>

Thanks for clarifying that. I’ll compile a list of destructive methods  
for myself, and be aware of them when I map.

Also learned not to trust blindly in the ! from another post.

---

<div class="post-metadata">

**Author:** ![bor\_S](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/0ea827/32.png) [@bor\_S](https://www.ruby-forum.com/u/bor_S)\
**Post date:** [December 14, 2007, 9:25am UTC](https://www.ruby-forum.com/t/suprised-by-destructive-map-not-map/123016/7 "2007-12-14T09:25:25Z")

</div>

2007/12/13, Boris S. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> Thanks for clarifying that. I’ll compile a list of destructive methods  
> for myself, and be aware of them when I map.
> 
> Also learned not to trust blindly in the ! from another post.

You should also remember that all parameters are passed by reference\*.  
Whether hazard occurs depends on what you do with them, especially  
the mutable ones. In your case elements in your Array were Arrays  
which are mutable. The issue does not happen if you used integers  
instead - even with \<\<:

irb(main):001:0\> a1 = [1,2]  
=\> [1, 2]  
irb(main):002:0\> a2 = a1.map {|x| x \<\< 1}  
=\> [2, 4]  
irb(main):003:0\> a1  
=\> [1, 2]

- Note: this is not 100% correct implementation wise but describes the  
situation properly from a Ruby user’s point of view.

Kind regards

robert

---

<div class="post-metadata">

**Author:** ![bor\_S](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/0ea827/32.png) [@bor\_S](https://www.ruby-forum.com/u/bor_S)\
**Post date:** [December 14, 2007, 10:02am UTC](https://www.ruby-forum.com/t/suprised-by-destructive-map-not-map/123016/8 "2007-12-14T10:02:20Z")

</div>

> irb(main):002:0\> array.map {|i| i \<\< 3}  
> =\> [[1, 3], [2, 3]]

Well, Array#\<\< means: **append** an element to an array and return  
the  
array. It does not mean make a copy of the array plus the new  
element.  
What you expected would be one of

```
array.map {|i| i.dup << 3}
array.map {|i| i + [3]}

```

It’s not necessary to use += here since the value of “i += [3]” is i,  
which you really don’t care about since you only want “i + [3]”.

Fixnum#\<\< means shift bits. Same symbol but totally different story.  
Also, numerics are no container classes but have immediate values,  
i.e.  
they are not passed by reference (according to Programming Ruby 2,  
p463), which is also why you can’t dup/clone them.

---

<div class="post-metadata">

**Author:** ![bor\_S](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/0ea827/32.png) [@bor\_S](https://www.ruby-forum.com/u/bor_S)\
**Post date:** [December 14, 2007, 12:42pm UTC](https://www.ruby-forum.com/t/suprised-by-destructive-map-not-map/123016/9 "2007-12-14T12:42:09Z")

</div>

> array.map{|a| a += [3]}
> 
> That expands to:
> 
> array.map{|a| a = a + [3] }

You might want to skip the useless assignment:

array.map{|a| a + [3]}

gegroet,  
Erik V. - [http://www.erikveen.dds.nl/](http://www.erikveen.dds.nl/)
