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.stack.imgur.com/wISqj.jpg.

Emre
  • 81
  • 8

3 Answers3

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.

Surya
  • 15,703
  • 3
  • 51
  • 74
  • 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 Jul 03 '17 at 12:45
  • @emre be careful that you don't end up writing C++ in Ruby. – David Aldridge Jul 05 '17 at 12:46
  • @david care to elaborate on that? I'm a bachelor doing his first internship so not really experienced. – Emre Jul 05 '17 at 13:26
  • @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 http://www.poodr.com for tips. The more OO you are, the better Ruby will treat you :) – David Aldridge Jul 05 '17 at 13:32
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.

Stefan
  • 109,145
  • 14
  • 143
  • 218
guitarman
  • 3,290
  • 1
  • 17
  • 27
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}] 
David Aldridge
  • 51,479
  • 8
  • 68
  • 96