Quick way to get attributes (without using collect)

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-forum.com/.

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

···

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?

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.

···

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?

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

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

cheers
max

···

--
Posted via http://www.ruby-forum.com/.

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

···

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)