Q: Ruby - Initializing and updating objects This is confusing me:
class Person 
    attr_accessor :id, :name

    def initialize(init)
        init.each_pair do |key, val|
            instance_variable_set('@' + key.to_s, val)
        end
    end
end

@adam = Person.new(:id => 1, :name => "Adam")
@eve = Person.new(:id => 2)

@people = [ @adam, @eve, nil ]

print @people

@people.map! do |person|
    person ||= Person.new(:id => 3, :name => "Some default")

    if person.name.nil?
        person.name = "Eve"
    end
end 

print @people 

# outputs 
# [#<Person:0x007ff184303900 @id=1, @name="Adam">, #<Person:0x007ff184303770 @id=2>, nil]
# [nil, "Some default", "Some default"]

I'd like to fully populate @people so that I end up with something along the lines of:
[#<Person:0x007ff184303900 @id=1, @name="Adam">, #<Person:0x007ff184303770 @id=2,  @name="Eve">, #<Person:0x007ff184301111 @id=3,  @name="Some default">]

What am I doing wrong?

A: What map does is it calls the block you supplied, passes an element of a collection to it and grabs value returned from the block. In absence of explicit return keyword, value of a block is the value of last evaluated expression. That would be the if expression in your example. It can return either nil or a name. And those values are mapped to the original collection. Fix is easy: just make sure that you return a Person from the block (by evaluating it last, for example).
@people.map! do |person|
  person ||= Person.new(:id => 3)

  if person.name.nil?
    person.name = "Some default"
  end
  person
end

print @people

# >> [#<Person:0x007fb5f288b288 @id=1, @name="Adam">, #<Person:0x007fb5f288b0f8 @id=2, @name="Some default">, #<Person:0x007fb5f288ae28 @id=3, @name="Some default">]


A: .map! replaces each element with whatever is returned from the .map! method (which in this case is person.name).  Add a "return person" at the end of your .map! method.  Or do a .each instead of .map
