Enumerable#find_yield, useful shortcut? better name?

I

Intransition

This method has long been suggested to me as #map_detect. At first I
wasn't sure of it's usefulness. But now I think it's okay. (Your
thoughts?). In any case, the name has to go. Currently I'm thinking
#find_yield. Any better suggestions?

module Enumerable

# Similar to #detect and #find. Yields each element to the block
# and returns the first result that evaluates as *true*,
# terminating early.
#
# obj1.foo? #=> false
# obj2.foo? #=> true
# obj2.foo #=> "value"
#
# [obj1, obj2].find_yield { |obj| obj.foo if obj.foo? } #=>
"value"
#
# If the block is never true, return the +fallback+ parameter,
# or nil if no +fallback+ is specified.
#
# [1,2,3].find_yield { |_| false } #=> nil
# [false].find_yield(1) { |_| false } #=> 1
#
def find_yield(fallback = nil) #:yield:
each do |member|
result = yield(member)
return result if result
end
fallback
end

end

Thanks.
 
A

Aaron Patterson

This method has long been suggested to me as #map_detect. At first I
wasn't sure of it's usefulness. But now I think it's okay. (Your
thoughts?). In any case, the name has to go. Currently I'm thinking
#find_yield. Any better suggestions?

I would call the method "unnecessary". Especially since we already have
a built in syntax which is more concise:

[1,2,3].find { |_| false } # => nil
[1,2,3].find { |_| false } || 1 # => 1
 
M

Martin DeMello

This method has long been suggested to me as #map_detect. At first I
wasn't sure of it's usefulness. But now I think it's okay. (Your
thoughts?). In any case, the name has to go. Currently I'm thinking
#find_yield. Any better suggestions?

#where doesn't read too badly

martin
 
B

Benoit Daloze

[Note: parts of this message were removed to make it a legal post.]

As Aaron,
I prefer the simple built-in method, with what u want to do with the object
after:

obj1.foo? #=> false
obj2.foo? #=> true
obj2.foo #=> "value"

[obj1, obj2].find_yield { |obj| obj.foo if obj.foo? } #=> "value"

[obj1, obj2].find { |obj| obj.foo? }.foo #=> "value"

Even more concise ...
The only problem I see is if we don't find, we'll call foo on nothing(nil).

So to have the same behavior:
x = [obj1, obj2].find(nil) { |obj| obj.foo? }
x.foo unless foo.nil? #=> "value"

And I think it's quite normal we have to manage with a structure(the if) how
to manage when we didn't find.

But I'm probably missing a special case. Could you give a example in
situation, to see if it is easier to read or not?

If you want to take several objects, you would use [obj1, obj2].select {
|obj| obj.foo? }.map {|e| e.foo}
So the idea of Robert Klemme is accurate, sure.
 
I

Intransition

As Aaron,
I prefer the simple built-in method, with what u want to do with the obje= ct
after:

obj1.foo? #=3D> false
obj2.foo? #=3D> true
obj2.foo =A0#=3D> "value"

[obj1, obj2].find_yield { |obj| obj.foo if obj.foo? } #=3D> "value"

[obj1, obj2].find { |obj| obj.foo? }.foo #=3D> "value"

Even more concise ...
The only problem I see is if we don't find, we'll call foo on nothing(nil= ).

So to have the same behavior:
x =3D [obj1, obj2].find(nil) { |obj| obj.foo? }
x.foo unless foo.nil? #=3D> "value"

And I think it's quite normal we have to manage with a structure(the if) = how
to manage when we didn't find.

But I'm probably missing a special case. Could you give a example in
situation, to see if it is easier to read or not?

I think you make a good point. The difference lies in where you are
able to put the logic. Eg.

x =3D [obj1, obj2].find{ |obj| obj.foo? }
y =3D x ? do_something_with(x.foo) : nil

vs.

y =3D [obj1, obj2].find_yield do |obj|
do_something_with(obj.foo) if obj.foo?
end

These are functionally equivalent, so really this is just a matter
constructional preference. If there were a way to make the former more
concise, then the case for the later would probably be moot, but I'm
not sure there is.
 
I

Intransition

#where doesn't read too badly

I agree. But to me #where also conveys the same meaning as #select.
Plus I tend to like it's use in higher-order messaging, and I have no
desire to tread on those toes. However, you did get me thinking in a
different direction which ultimately led me to #found, which conveys
it's relation to #find and that we want, not the element itself, but
what was "found". Though I admit, it has a strange tense for a method
name.
 
B

Brian Candler

Unless I have misunderstood, I think there is an almost identical
facility built-in:
[1,2,3].find(lambda {-1}) { |x| x > 10 }
=> -1

It's slightly inconvenient to require a proc for the default, but it
means you can create an 'expensive' object or modify the collection with
a side-effect:
a = [1,2,3] => [1, 2, 3]
a.find(lambda{ a << 99; a.last }) { |x| x > 10 } => 99
a => [1, 2, 3, 99]
 

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,774
Messages
2,569,596
Members
45,128
Latest member
ElwoodPhil
Top