Reputation: 11
when I run this code in ruby:
updated_users = []
users = [{:name => "sam" , :number => 001 }]
ids = ["aa" , "bb" , "cc"]
users.each do |user|
ids.each do |id|
user[:guid] = id
updated_users << user
end
end
p updated_users
I get:
[{:name=>"sam", :number=>1, :guid=>"cc"},
{:name=>"sam", :number=>1, :guid=>"cc"},
{:name=>"sam", :number=>1, :guid=>"cc"}]
I expected to get:
[{:name=>"sam", :number=>1, :guid=>"aa"},
{:name=>"sam", :number=>1, :guid=>"bb"},
{:name=>"sam", :number=>1, :guid=>"cc"}]
What is happening and how should I get the desired output?
Upvotes: 1
Views: 86
Reputation: 110755
Your question reflects a common misunderstanding held by budding Rubyists. You begin with the following:
updated_users = []
users = [{:name => "sam" , :number => 001 }]
ids = ["aa" , "bb" , "cc"]
users
is an array containing a single hash. Let's note the id of that hash object:
users.first.object_id
#=> 1440
Now let's execute your code with some puts
statements added to see what is going on.
users.each do |user|
puts "user = #{user}"
puts "user.object_id = #{user.object_id}"
ids.each do |id|
puts "\nid = #{id}"
user[:guid] = id
puts "user = #{user}"
puts "user.object_id = #{user.object_id}"
updated_users << user
puts "updated_users = #{updated_users}"
updated_users.size.times do |i|
puts "updated_users[#{i}].object_id = #{updated_users[i].object_id}"
end
end
end
This displays the following.
user = {:name=>"sam", :number=>1}
user.object_id = 1440
So far, so good. Now begin the ids.each
loop:
id = aa
user = {:name=>"sam", :number=>1, :guid=>"aa"}
user.object_id = 1440
As user
did not previously have a key :guid
, the key-value pair :guid=>"aa"
was added to user
. Note that user
's id has not changed. user
is then appended to the (empty) array updated_users
:
updated_users = [{:name=>"sam", :number=>1, :guid=>"aa"}]
updated_users[0].object_id = 1440
Again this is what we should expect. The next element of ids
is then processed:
id = bb
user = {:name=>"sam", :number=>1, :guid=>"bb"}
user.object_id = 1440
Since user
has a key :guid
this merely changes the value of that key from "aa"
to "bb"
. user
is then appended to updated_users
:
updated_users = [{:name=>"sam", :number=>1, :guid=>"bb"},
{:name=>"sam", :number=>1, :guid=>"bb"}]
updated_users[0].object_id = 1440
updated_users[1].object_id = 1440
The first and second elements of this array are seen to be the same object user
, so changing the value of :user
's key :guid
affected both elements in the same way.
The same thing happens when the third and last element of ids
is processed:
id = cc
user = {:name=>"sam", :number=>1, :guid=>"cc"}
user.object_id = 1440
updated_users = [{:name=>"sam", :number=>1, :guid=>"cc"},
{:name=>"sam", :number=>1, :guid=>"cc"},
{:name=>"sam", :number=>1, :guid=>"cc"}]
updated_users[0].object_id = 1440
updated_users[1].object_id = 1440
updated_users[2].object_id = 1440
Got it?
To obtain the result you want you need to append updated_users
with distinct hashes derived from user
:
users.each do |user|
ids.each do |id|
updated_users << user.merge(guid: id)
end
end
updated_users
#=> [{:name=>"sam", :number=>1, :guid=>"aa"},
# {:name=>"sam", :number=>1, :guid=>"bb"},
# {:name=>"sam", :number=>1, :guid=>"cc"}]
Note that users
has not been mutated (changed):
users
#=> [{:name=>"sam", :number=>1}]
See Hash#merge. Note that user.merge(guid: id)
is shorthand for user.merge({ guid: id })
or, equivalently, user.merge({ :guid => id })
.
Upvotes: 5