Odd behavior when << hash

A

Alex Stahl

Hi Folks - Using Ruby 1.8.7, I found this oddity:

require 'rubygems'
require 'json'

setup = []
ids = ["12345", "54321", "21345", "548752", "3215", "50203"]

doIt = {
"action" => "do something",
"result" => "true"
}

cnt = rand(10)
1.upto(cnt) do

doIt['params'] = ids[rand(6)]
p "Pushing step: #{doIt.inspect}"
setup << doIt
end

p "Final steps list: #{setup.inspect}"

**************************************

When I inspect my final steps list at the end, the values of
'doIt['params']' is always the same - set to the last value of the last
run of the upto loop. But when I inspect doIt prior to pushing it, I
can see that it has its own value, acquired randomly.

So my question is, why is the hash not being pushed onto the array
properly? What side effect am I encountering here and how do I get
around it?

(I've already tried declaring "params" => "" in the initial hash
creation; that didn't help).

Thanks,
Alex

PS - You'd be correct in noting this code doesn't do anything useful.
I've stripped away other parts to isolate the questionable behavior for
demonstration purposes.
 
R

Rob Biedenharn

Hi Folks - Using Ruby 1.8.7, I found this oddity:

require 'rubygems'
require 'json'

setup = []
ids = ["12345", "54321", "21345", "548752", "3215", "50203"]

doIt = {
"action" => "do something",
"result" => "true"
}

# so here, doIt is referencing an object which is a hash
cnt = rand(10)
1.upto(cnt) do

doIt['params'] = ids[rand(6)]
p "Pushing step: #{doIt.inspect}"
setup << doIt

# this puts that reference into the setup array
# NOTE: it is always referring to the same hash
end

p "Final steps list: #{setup.inspect}"

**************************************

When I inspect my final steps list at the end, the values of
'doIt['params']' is always the same - set to the last value of the
last
run of the upto loop. But when I inspect doIt prior to pushing it, I
can see that it has its own value, acquired randomly.

So my question is, why is the hash not being pushed onto the array
properly? What side effect am I encountering here and how do I get
around it?

(I've already tried declaring "params" => "" in the initial hash
creation; that didn't help).

Thanks,
Alex

PS - You'd be correct in noting this code doesn't do anything useful.
I've stripped away other parts to isolate the questionable behavior
for
demonstration purposes.


Try creating the hash object inside the loop (i.e., creating a new
object each time)

rand(10).times do
setup << { "action" => "do something",
"result" => "true",
"params" => ids[rand(6)].dup
}
end
puts "Final steps list: #{setup.inspect}"


Note that I'm also calling .dup on the element taken from the ids
array so there's a separate string object. You can apply this to your
real code, I hope.

-Rob

Rob Biedenharn
(e-mail address removed) http://AgileConsultingLLC.com/
(e-mail address removed) http://GaslightSoftware.com/
 
A

Alex Stahl

Thanks, Rob. Made the mistake of assuming I get a copy of the hash when
I push it. IIRC, there are other (not-so-elegant) languages that do so.

Appreciate the point about .dup as well, but since at the end of this,
I'm just writing everything out to a file, and the elements of ids will
never change (at least not in this script), I don't think I need a
duplicate string.


Hi Folks - Using Ruby 1.8.7, I found this oddity:

require 'rubygems'
require 'json'

setup = []
ids = ["12345", "54321", "21345", "548752", "3215", "50203"]

doIt = {
"action" => "do something",
"result" => "true"
}

# so here, doIt is referencing an object which is a hash
cnt = rand(10)
1.upto(cnt) do

doIt['params'] = ids[rand(6)]
p "Pushing step: #{doIt.inspect}"
setup << doIt

# this puts that reference into the setup array
# NOTE: it is always referring to the same hash
end

p "Final steps list: #{setup.inspect}"

**************************************

When I inspect my final steps list at the end, the values of
'doIt['params']' is always the same - set to the last value of the
last
run of the upto loop. But when I inspect doIt prior to pushing it, I
can see that it has its own value, acquired randomly.

So my question is, why is the hash not being pushed onto the array
properly? What side effect am I encountering here and how do I get
around it?

(I've already tried declaring "params" => "" in the initial hash
creation; that didn't help).

Thanks,
Alex

PS - You'd be correct in noting this code doesn't do anything useful.
I've stripped away other parts to isolate the questionable behavior
for
demonstration purposes.


Try creating the hash object inside the loop (i.e., creating a new
object each time)

rand(10).times do
setup << { "action" => "do something",
"result" => "true",
"params" => ids[rand(6)].dup
}
end
puts "Final steps list: #{setup.inspect}"


Note that I'm also calling .dup on the element taken from the ids
array so there's a separate string object. You can apply this to your
real code, I hope.

-Rob

Rob Biedenharn
(e-mail address removed) http://AgileConsultingLLC.com/
(e-mail address removed) http://GaslightSoftware.com/
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

Hi Folks - Using Ruby 1.8.7, I found this oddity:

require 'rubygems'
require 'json'

setup = []
ids = ["12345", "54321", "21345", "548752", "3215", "50203"]

doIt = {
"action" => "do something",
"result" => "true"
}

cnt = rand(10)
1.upto(cnt) do

doIt['params'] = ids[rand(6)]
p "Pushing step: #{doIt.inspect}"
setup << doIt
end

p "Final steps list: #{setup.inspect}"

**************************************

When I inspect my final steps list at the end, the values of
'doIt['params']' is always the same - set to the last value of the last
run of the upto loop. But when I inspect doIt prior to pushing it, I
can see that it has its own value, acquired randomly.

So my question is, why is the hash not being pushed onto the array
properly? What side effect am I encountering here and how do I get
around it?

(I've already tried declaring "params" => "" in the initial hash
creation; that didn't help).

Thanks,
Alex

PS - You'd be correct in noting this code doesn't do anything useful.
I've stripped away other parts to isolate the questionable behavior for
demonstration purposes.
You are modifying the same hash, then adding the same hash to the array
again. In other words, there is only ever one hash in your program (and data
is mutable in Ruby).

To analogize, if you record my address ten times, painting my shutters a
different after each, then decide to visit each of the ten addresses that
you have written down, you will find that all of the houses you visit will
have the same colour of shutters as the final time you painted my house.

I have modified the program to hopefully make this clearer.
http://codepad.org/ZPB7UC6Q
 
R

Robert Klemme

Thanks, Rob. Made the mistake of assuming I get a copy of the hash when
I push it. IIRC, there are other (not-so-elegant) languages that do so.

Appreciate the point about .dup as well, but since at the end of this,
I'm just writing everything out to a file, and the elements of ids will
never change (at least not in this script), I don't think I need a
duplicate string.

To complement the excellent explanations you have been given already:
the technical term for the effect is "aliasing".

http://en.wikipedia.org/wiki/Aliasing_(computing)#Aliased_pointers

Kind regards

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,968
Messages
2,570,153
Members
46,699
Latest member
AnneRosen

Latest Threads

Top