Re: Enumerable#collect and #select best practices


#1

-----Original Message-----
From: Daniel S. [mailto:removed_email_address@domain.invalid]

I need to collect the results of calling a method on each object in an

Enumerable that defines that method. Currently, I have this:

enum.collect{|obj| obj.a_method rescue nil }.compact

though this could also work:

This looks like prime .inject territory

enum.inject([]) do |l,obj|
(l << obj.a_method) if obj.respond_to? :a_method
end


#2

Joe B. wrote:

This looks like prime .inject territory

enum.inject([]) do |l,obj|
(l << obj.a_method) if obj.respond_to? :a_method
end

Wonderful!

Now, what if I wanted to generalize it? That is, a method on Enumerable
that returns an array of the results of calling a a given block with
each item in the array, excluding the items that do not respond to that
method.

This is what I’ve got:

module Enumerable
# need a better name…
def collect_x
collect do |obj|
begin
yield obj
rescue NoMethodError
nil
end
end.compact
end
end

Though it’s still verbose…


#3

On May 16, 2006, at 2:48 PM, Daniel S. wrote:

given block with each item in the array, excluding the items that
rescue NoMethodError
nil
end
end.compact
end
end

Though it’s still verbose…

I don’t think you should use #compact. What if #a_method happens to
return nil as a result? Also rescuing NoMethodError, is risky if you
have a bug inside #a_method.

I would write it:

module Enumerable
def selective_collect(msg_name, *args)
inject([]) do |result_list, value|
if value.respond_to? msg_name
result_list << value.send(msg_name, *args)
end
result_list
end
end
end

x = [ … ]
x.selective_collect(:a_method)

of x.selective_collect(:another_method, “hello”)


#4

Logan C. wrote:

       result_list << value.send(msg_name, *args)
    end
    result_list
  end

end
end

x = [ … ]
x.selective_collect(:a_method)

of x.selective_collect(:another_method, “hello”)

Yeah, I made a similar implementation – it just doesn’t feel Rubyish
enough not to pass a block to an iterator…

Well, I guess it’s a border case; a general solution is probably not
needed.

Thanks for your response,
Daniel


#5

In Message-Id: removed_email_address@domain.invalid
Logan C. removed_email_address@domain.invalid writes:

I don’t think you should use #compact. What if #a_method happens to
return nil as a result? Also rescuing NoMethodError, is risky if you
have a bug inside #a_method.

And if your block given to collect is fairly complex, needless work
may affects scalability of code. Ie. If the number of items will be
selected
are small and a source array is very huge — say, 100 : 10,000,000
— useless 9,999,900 invocations are avoided if “select then collect”
approach.