Forum: Ruby on Rails strange thing w/ array.map

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Clem R. (Guest)
on 2007-01-25 09:01
Hello,

    I'm writing a method that builds rss and atom feeds and I've noticed
something strange when I try updating the content array by using a map.

I'm new to ruby and must confess that I don't know how array maps work
so well.

I am formatting some of the rss content array by updating it's contents
w/ a map like so:

[code]
@entities = Space.find :all

@new_entities = @entities.map do |entity|
  open_closed = entity.complete? ? "[x] " : "[ ] "
  entity.title = open_closed + entity.title
  entity.title += (' due on ' + local_date_time(entity.due_on)) if
entity.due_on
  entity.title += " (#{entity.status_title})"
end if @entities
[/code]

then when I try to use the @new_entities array in the feed, I get an
error

[code]
for entity in @new_entities  #-->>error!
  xml.item do
    xml.title(entity.title)
    xml.description(entity.description)
    xml.pubDate(entity.date.strftime("%a, %d %b %Y %H:%M:%S %z"))
    xml.link("http://www.recentrambles.com/pragmatic/view/" +
entity.id.to_s)
    xml.guid("http://www.recentrambles.com/pragmatic/view/" +
entity.id.to_s)
  end
end
[/code]

but, if I use the original @entities array it works perfectly
[code]
for entity in @entities   #-->>works!
  xml.item do
    xml.title(entity.title)
    xml.description(entity.description)
    xml.pubDate(entity.date.strftime("%a, %d %b %Y %H:%M:%S %z"))
    xml.link("http://www.recentrambles.com/pragmatic/view/" +
entity.id.to_s)
    xml.guid("http://www.recentrambles.com/pragmatic/view/" +
entity.id.to_s)
  end
end
[/code]


How, exactly, is that @entities array getting updated by the map?

Thanks for your help!
Chris H. (Guest)
on 2007-01-25 17:10
(Received via mailing list)
2 things are going on here

1) i don't believe you are getting back what you think you are getting
back from .map
2) the entity you are working with in your block is the actual entity,
not a copy.  so you are basically overwriting your original array of
entities

.map returns a new array based on whatever it is you do in within the
block

[1,2,3].map { |x| x*3 } #=> [3,6,9]

in your case the last statement in the block is

entity.title += " (#{entity.status_title})"

so your block is going to return whatever that evaluates to, not the
entity object you are expecting.

in addition, your block is actually overwriting all the values of the
@entities objects you are passing in.  if you were to compare the
titles of the @entities objects before and then after .map is called,
you're going to see that they are different.

ex:

class Thing
  attr_accessor :foo
end

a = Thing.new
a.foo = "abc"
b = Thing.new
b.foo = "xyz"

x = [a,b].map { |c| c.foo += " modified" }
a.foo # => "abc modified"
b.foo # => "xyz modified"
x = ["abc modified", "xyz modified"] # you expected this to be an
array of Thing objects

if you do want to overwrite the attributes of your objects, just use
.each

@entities.each do |entity|
  ...
end
Clem R. (Guest)
on 2007-01-25 21:39
Chris,

   Thanks for your help - this is great.

Ruby does things I never expect - in the best of ways.

Cheers!




Chris H. wrote:
> 2 things are going on here
>
> 1) i don't believe you are getting back what you think you are getting
> back from .map
> 2) the entity you are working with in your block is the actual entity,
> not a copy.  so you are basically overwriting your original array of
> entities
>
> .map returns a new array based on whatever it is you do in within the
> block
>
> [1,2,3].map { |x| x*3 } #=> [3,6,9]
>
> in your case the last statement in the block is
>
> entity.title += " (#{entity.status_title})"
>
> so your block is going to return whatever that evaluates to, not the
> entity object you are expecting.
>
> in addition, your block is actually overwriting all the values of the
> @entities objects you are passing in.  if you were to compare the
> titles of the @entities objects before and then after .map is called,
> you're going to see that they are different.
>
> ex:
>
> class Thing
>   attr_accessor :foo
> end
>
> a = Thing.new
> a.foo = "abc"
> b = Thing.new
> b.foo = "xyz"
>
> x = [a,b].map { |c| c.foo += " modified" }
> a.foo # => "abc modified"
> b.foo # => "xyz modified"
> x = ["abc modified", "xyz modified"] # you expected this to be an
> array of Thing objects
>
> if you do want to overwrite the attributes of your objects, just use
> .each
>
> @entities.each do |entity|
>   ...
> end
This topic is locked and can not be replied to.