Quick way to get attributes (without using collect)

M

Max Williams

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
 
F

Farrel Lifson

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
 
J

Jano Svitok

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.
 
S

Sebastian Hungerecker

Max said:
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
 
M

Max Williams

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

cheers
max
 
B

Brian Adkins

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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top