Edward
Edward

Reputation: 3499

Ruby - looping through an array, appending a hash - odd behaviour

I'm confused about the way an array is updated when I loop through it. Here's a made up example that shows the behaviour.

people = [{"name"=>"Edward", "age" =>"43", "height"=>"tallish"},
          {"name"=>"Ralph", "age" =>"40", "height"=>"medium heigth"},
          {"name"=>"George", "age" =>"35", "height"=>"very tall"},
          {"name"=>"Mark", "age" =>"25", "height"=>"short"}]
numbers = ["1","3","26"]
new_array = []

numbers.each do |number|
    people.each do |person|
        person["name"] = person["name"] +" "+ number
        new_array << person
    end
end

At the end of that new_array is

[{"name"=>"Edward 1 3 26", "age"=>"43", "height"=>"tallish"},
{"name"=>"Ralph 1 3 26", "age"=>"40", "height"=>"medium heigth"},
{"name"=>"George 1 3 26", "age"=>"35", "height"=>"very tall"},
{"name"=>"Mark 1 3 26", "age"=>"25", "height"=>"short"},
{"name"=>"Edward 1 3 26", "age"=>"43", "height"=>"tallish"},
{"name"=>"Ralph 1 3 26", "age"=>"40", "height"=>"medium heigth"},
{"name"=>"George 1 3 26", "age"=>"35", "height"=>"very tall"},
{"name"=>"Mark 1 3 26", "age"=>"25", "height"=>"short"},
{"name"=>"Edward 1 3 26", "age"=>"43", "height"=>"tallish"},
{"name"=>"Ralph 1 3 26", "age"=>"40", "height"=>"medium heigth"},
{"name"=>"George 1 3 26", "age"=>"35", "height"=>"very tall"},
{"name"=>"Mark 1 3 26", "age"=>"25", "height"=>"short"}]

Each person appears three times, which I what I expected and wanted. BUT their name is the same each time. I expected name to be "Edward 1" the first time, then "Edward 1 3" and finally "Edward 1 3 26"

What's going on here? I thought the loop would append each separate hash onto new_array, rather than 3 all the same.

Upvotes: 1

Views: 1014

Answers (2)

megas
megas

Reputation: 21791

You can convert your code a little bit to see the process

numbers.each do |number|
    people.each do |person|
        person["name"] = person["name"] +" "+ number
        new_array << person
        puts person["name"]
    end
end

You will get this result:

Edward 1
...
Edward 1 3
...
Edward 1 3 26
...

As you can see the algorithm works almost as you expected. But person["name"] is reference to only one object(string), that's why the final result has the last string Edward 1 3 26

EDIT: To get your what you wanted you should create new object every time

numbers.each do |number|
  people.each do |person|
    person["name"] = person["name"] +" "+ number
    new_array << person.dup
  end
end

Don't forget reinitialize the people variable because this expression

person["name"] = person["name"] +" "+ number

modifies the people variable.

Upvotes: 0

Chowlett
Chowlett

Reputation: 46667

people.each is providing you with a reference to each entry in people, so when you do person["name"] =... you're modifying the original array.

Try this:

numbers.each do |number|
    people.each do |person|
        new_person = person.dup
        new_person["name"] << " " + number
        new_array << new_person
    end
end

Upvotes: 1

Related Questions