0

While iterating, I am saving some data to a hash each time. Within the same loop, I push the hash to an array.

The below code does not work, the last hash object overwrites all the other ones in the array.

playlists = []
aPlaylist = {}

while (count < 3)
    #some code... produces the hash "aPlaylist"
    playlist << aPlaylist
end

The code below does work. Why, and what is the difference?

playlists = []

while (count < 3)
    aPlaylist = {}
    #some code... produces the hash "aPlaylist"
    playlist << aPlaylist
end

Here are the correct and wrong outputs (converted to csv): https://i.sstatic.net/wISqj.jpg.

3
  • "the last hash object overwrites all the other ones in the array" – could you show the erroneous result? Commented Jul 3, 2017 at 12:17
  • This depends entirely on the "some code" you left out. Commented Jul 3, 2017 at 12:22
  • I updated the question with images. @JörgWMittag I don't think so, the other answers explain why. Commented Jul 3, 2017 at 12:59

3 Answers 3

2

Because, in the first case, the object is same that is on 0, 1, and 2nd index.

playlist = []
aPlaylist = {}
count = 0

while (count < 3)
    #some code... produces the hash "aPlaylist"
    playlist << aPlaylist
    puts aPlaylist.object_id
    count += 1
end
#=> 2048
#=> 2048
#=> 2048

While in second case it changes:

playlist = []

count = 0

while (count < 3)
    aPlaylist = {}
    #some code... produces the hash "aPlaylist"
    playlist << aPlaylist
    puts aPlaylist.object_id
    count += 1
end
#=> 2048
#=> 2038
#=> 2028

Which is why from second case when you make changes to hash, it does not get reflected in all places in array.

Read this stackoverflow answer for more detail.

Sign up to request clarification or add additional context in comments.

4 Comments

Oh I see now. So basically as I update aPlaylist in each iteration, it is sort of a pass-by-reference to the array and they change too. This is a bit unintuitive for me, coming from C++ background.
@emre be careful that you don't end up writing C++ in Ruby.
@david care to elaborate on that? I'm a bachelor doing his first internship so not really experienced.
@emre I added an answer with an idiomatic Ruby methodology, so please see that. while loops and incrementing counters are pretty rare in Ruby, where map/reduce and other such methodologies are more common. I'd recommend any talk by Sandy Metz (various YouTube ones available) and her book poodr.com for tips. The more OO you are, the better Ruby will treat you :)
0

aPlaylist = {} creates a hash and aPlaylist variable holds a pointer to the hash object.

In your first example you edit only this one hash object.

aPlaylist = {}
count = 0
while (count < 3)
  puts aPlaylist.object_id
  count += 1
end
#=> 70179174789100
#=> 70179174789100
#=> 70179174789100

In your second example you create a new hash object within each iteration. That's way this code works.

count = 0
while (count < 3)
  aPlaylist = {}
  puts aPlaylist.object_id
  count += 1
end
#=> 70179182889040
#=> 70179182888980
#=> 70179182888920

Have a look to the printed object-ids.

Comments

0

I think an idiomatic Ruby approach would be something like ...

playlist = 0.upto(2).map{|count| something_that_returns_a_hash }

... or ...

playlist = (0..2).map{|count| something_that_returns_a_hash }

Hence:

0.upto(2).map{|count| {count => count} }

[{0=>0}, {1=>1}, {2=>2}] 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.