# Why does this work?

**URL:** <https://www.ruby-forum.com/t/why-does-this-work/70752>\
**Category:** Rails\
**Created:** [October 2, 2006, 5:07pm UTC](https://www.ruby-forum.com/t/why-does-this-work/70752 "2006-10-02T17:07:32Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Bryan\_M](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/f6c823/32.png) [@Bryan\_M](https://www.ruby-forum.com/u/Bryan_M)\
**Post date:** [October 2, 2006, 5:07pm UTC](https://www.ruby-forum.com/t/why-does-this-work/70752/1 "2006-10-02T17:07:32Z")

</div>

Hi. I’m still getting adjusted to Rails coding, and and stumbled into a  
piece of code that in my mind, shouldn’t work, but it does. I know Rails  
is smart, but…take a look.

I’m trying to populate a select box with first and last names, which are  
stored in separate fields: employee.lname and employee.fname.

@employees = Employee.find(:all, :order =\> “name”)

I wanted the box to display “lname, fname” and tried to use the  
collection\_select method to easily assign :id to each option value. I  
assumed using collection\_select wouldn’t work because it only lets me  
specify one field for option text. So I tried making a new hash with a  
formatted string:

@users = Array.new  
@employees.each do |employee|  
@users.push([employee.id =\> employee.lname \<\< ", " \<\< employee.fname])  
end

And in my template, I have:

\<%= collection\_select(:profile, :user, @employees, :id, :lname) %\>

And lo and behold, I get a text box with each name formatted like  
“Smith, John.” But I don’t see how its possible, since in my  
collection\_select statement, I reference the original @employees hash  
and not @users. I also only specify :lname as a parameter. But if I take  
out the each loop, it goes back to only listing the last name. What  
gives? The only explanation I can think of is if the @employees.each  
loop is modifying @employees hash itself.

---

<div class="post-metadata">

**Author:** ![Bryan\_M](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/f6c823/32.png) [@Bryan\_M](https://www.ruby-forum.com/u/Bryan_M)\
**Post date:** [October 2, 2006, 5:27pm UTC](https://www.ruby-forum.com/t/why-does-this-work/70752/2 "2006-10-02T17:27:18Z")

</div>

Your suspicion is correct. “\<\<” is a “destructive” operator, so your:

> @users.push([employee.id =\> employee.lname \<\< ", " \<\< employee.fname])

is actually pushing “,” and employee.fname INTO employee.lname,  
destroying its original value.

On 10/2/06, Bryan M [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> I wanted the box to display “lname, fname” and tried to use the  
> And in my template, I have:
> 
> –  
> Posted via [http://www.ruby-forum.com/](http://www.ruby-forum.com/).
> 
> >

–  
[The Test of Negation] Don’t include a sentence in documentation if its  
negation is obviously false. – Bob Martin

---

<div class="post-metadata">

**Author:** ![Bryan\_M](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/f6c823/32.png) [@Bryan\_M](https://www.ruby-forum.com/u/Bryan_M)\
**Post date:** [October 2, 2006, 5:27pm UTC](https://www.ruby-forum.com/t/why-does-this-work/70752/3 "2006-10-02T17:27:18Z")

</div>

Bryan M wrote:

> “Smith, John.” But I don’t see how its possible, since in my  
> collection\_select statement, I reference the original @employees hash  
> and not @users. I also only specify :lname as a parameter. But if I take  
> out the each loop, it goes back to only listing the last name. What  
> gives? The only explanation I can think of is if the @employees.each  
> loop is modifying @employees hash itself.

It is. The ‘\<\<’ method of String doesn’t just return the new  
concatenated string, it actually modifies the original string as well.

So if I have

a = “Hello”  
a \<\< " world"

then a will now contain “Hello world”.

In your iterator, you’re actually adding ", " and employee.fname to  
employee.lname for each employee. Good job you weren’t calling  
employee.save anywhere in there!

If you want to concatenate strings without this side effect, you could  
do something like:

@users.push(employee.id =\> “#{employee.lname}, #{employee.fname}”)

Chris

---

<div class="post-metadata">

**Author:** ![Bryan\_M](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/f6c823/32.png) [@Bryan\_M](https://www.ruby-forum.com/u/Bryan_M)\
**Post date:** [October 2, 2006, 5:29pm UTC](https://www.ruby-forum.com/t/why-does-this-work/70752/4 "2006-10-02T17:29:19Z")

</div>

Chris M. wrote:

> Bryan M wrote:
> 
> > @users = Array.new  
> > @employees.each do |employee|  
> > @users.push([employee.id =\> employee.lname \<\< ", " \<\< employee.fname])  
> > end
> 
> If you want to concatenate strings without this side effect, you could  
> do something like:
> 
> @users.push(employee.id =\> “#{employee.lname}, #{employee.fname}”)

Of course, if you want to be really Rubyish, you could replace that  
whole lot with:

@users = @employees.collect {|e| [e.id, “#{employee.lname},  
#{employee.fname}”]}

Chris

---

<div class="post-metadata">

**Author:** ![Bryan\_M](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/b/f6c823/32.png) [@Bryan\_M](https://www.ruby-forum.com/u/Bryan_M)\
**Post date:** [October 2, 2006, 5:40pm UTC](https://www.ruby-forum.com/t/why-does-this-work/70752/5 "2006-10-02T17:40:53Z")

</div>

Ah. Thanks for all your (really quick) replies. That certainly makes  
sense. I didn’t really read up on concat operators before choosing \<\<.  
So it was kind of a happy accident, but I’ll definately follow your  
suggestions to make it less destructive.

Bryan
