0

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?

2
  • How on earth were you hoping to set @eve's name to "Eve"? I don't see that. Commented May 29, 2013 at 15:32
  • 1
    .map! will replace each element of your array with whatever the method returns (the last executed statement). Try .each instead, or do a "return person" at the end of your .map! method Commented May 29, 2013 at 15:35

2 Answers 2

2

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">]
Sign up to request clarification or add additional context in comments.

4 Comments

+1 That works for me. Really annoying when you post a complete code sample and then the only thing that needed changing was a return!
Your code could benefit from little improvements, though. But you should pick up the feeling for idiomaticity as you gain more experience.
pretty neat. I assume send("#{k}=", v) is operating on 'self' which would be 'init', right?
@SnowCrash: No, it would a instance of Person. Loops like each don't introduce new scope.
1

.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

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.