[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Quick way to get attributes (without using collect

Max Williams

11/20/2007 5:04:00 PM

This was inspired by Rails, but i think it's common Ruby question. Say
i have an array of Employee objects, which have an attribute "name". I
want to get all the names, as an array.

Let's say i already have the variable "employees", which points to an
array of Employee objects.

What i want to do is something like this -

employee_names = employees.names

But what i have to keep on doing is something like this -

employee_names = employees.collect{ |emp| emp.name}

Now, this isn't a *massive* hassle, and i find Array#collect incredibly
useful, but it's a bit annoying to have to keep typing it again and
again. Is there a way to set it up so that

foos

is automatically treated as

collect { |x| x.foo }

?

Or another way to do this simply?

thanks
max
--
Posted via http://www.ruby-....

5 Answers

Farrel Lifson

11/20/2007 5:12:00 PM

0

On 20/11/2007, Max Williams <toastkid.williams@gmail.com> wrote:
> This was inspired by Rails, but i think it's common Ruby question. Say
> i have an array of Employee objects, which have an attribute "name". I
> want to get all the names, as an array.
>
> Let's say i already have the variable "employees", which points to an
> array of Employee objects.
>
> What i want to do is something like this -
>
> employee_names = employees.names
>
> But what i have to keep on doing is something like this -
>
> employee_names = employees.collect{ |emp| emp.name}
>
> Now, this isn't a *massive* hassle, and i find Array#collect incredibly
> useful, but it's a bit annoying to have to keep typing it again and
> again. Is there a way to set it up so that
>
> .foos
>
> is automatically treated as
>
> .collect { |x| x.foo }
>
> ?
>
> Or another way to do this simply?

You can use the Symbol#to_proc method

class Symbol
def to_proc
Proc.new {|e| e.send(self)}
end
end

Which will let you right
employee_names = employees.collect(&:name)

Farrel

Jano Svitok

11/20/2007 5:17:00 PM

0

On Nov 20, 2007 6:03 PM, Max Williams <toastkid.williams@gmail.com> wrote:
> This was inspired by Rails, but i think it's common Ruby question. Say
> i have an array of Employee objects, which have an attribute "name". I
> want to get all the names, as an array.
>
> Let's say i already have the variable "employees", which points to an
> array of Employee objects.
>
> What i want to do is something like this -
>
> employee_names = employees.names
>
> But what i have to keep on doing is something like this -
>
> employee_names = employees.collect{ |emp| emp.name}
>
> Now, this isn't a *massive* hassle, and i find Array#collect incredibly
> useful, but it's a bit annoying to have to keep typing it again and
> again. Is there a way to set it up so that
>
> .foos
>
> is automatically treated as
>
> .collect { |x| x.foo }
>
> ?
>
> Or another way to do this simply?

Let me introduce to you #method_missing. It's being called when the
method called is not found. The standard implementation just raises
NoMethodError,
but you can do anything there. In fact, this is how Rails does it's
find_by_this_and_that magic.

So, this is the idea (not tested, just written in the mail):

class Array
def method_missing(name, *args)
single_method = name.to_s.chop # 1. name is a symbol, 2. chop for simplicity
if !empty? && first.respond_to?(single_method)
collect { |x| x.send(single_method, *args) } # do whatever you want here
else
super # inherited implementation
end
end
end

Just make sure that this will not break the whole system (check for
's', and look any methods that might end with it, etc.)

Lookup the code in the Rails' sources, most probably you'll find there
nice singular/plural conversion etc.

Sebastian Hungerecker

11/20/2007 5:25:00 PM

0

Max Williams wrote:
> This was inspired by Rails, but i think it's common Ruby question.
> [...]
> What i want to do is something like this -
>
> employee_names = employees.names
>
> But what i have to keep on doing is something like this -
>
> employee_names = employees.collect{ |emp| emp.name}

In rails you can do "employee_names = employees.collect(&:name)" which is
somewhat more concise (though it incurs a performance penalty, I am told).

> Is there a way to set it up so that
> .foos
> is automatically treated as
> .collect { |x| x.foo }

module Enumerable
def method_missing(meth, *args, &blk)
map {|x| x.send(meth,*args, &blk)}
end
end

This might easily lead to bugs though if you want to invoke method on the
elements of the enum, not considering or not knowing that the enum itself
implements that method as well. For example:
[2,4,6] / 2 #=> [1, 2, 3]
But:
[2,4,6] * 2 #=> [2,4,6,2,4,6]

Or:
["1","2","3"].to_i #=> [1, 2, 3]
But:
[1,2,3].to_s #=> "123"


HTH,
Sebastian
--
Jabber: sepp2k@jabber.org
ICQ: 205544826

Max Williams

11/20/2007 5:31:00 PM

0

Thanks guys - i'll try the method missing approach, and also the rails
built in slightly shorter version :)

cheers
max
--
Posted via http://www.ruby-....

Brian Adkins

11/20/2007 5:55:00 PM

0

On Nov 20, 12:12 pm, Farrel Lifson <farrel.lif...@gmail.com> wrote:
> On 20/11/2007, Max Williams <toastkid.willi...@gmail.com> wrote:
> > Say
> > i have an array of Employee objects, which have an attribute "name". I
> > want to get all the names, as an array.
> > ...
> > What i want to do is something like this -
>
> > employee_names = employees.names
>
> > But what i have to keep on doing is something like this -
>
> > employee_names = employees.collect{ |emp| emp.name}
>
> You can use the Symbol#to_proc method
>
> class Symbol
> def to_proc
> Proc.new {|e| e.send(self)}
> end
> end
>
> Which will let you right
> employee_names = employees.collect(&:name)

map is shorter than collect. Also, adding optional args to to_proc
will make it more generally useful.

class Symbol
def to_proc
lambda {|obj, *args| obj.send(self, *args) }
end
end

employee_names = employees.map(&:name)

I'd be wary of using method_missing for this.

Brian Adkins