Enumerable#find_yield, useful shortcut? better name?

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.

On Fri, Nov 13, 2009 at 01:07:41AM +0900, Intransition wrote:

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

On 12.11.2009 17:41, Aaron P. wrote:

[1,2,3].find { |_| false } || 1 # => 1
I disagree. The difference between TMISFAN (the method in search for a
name) and your solution is that TMISFAN returns the result of the block
evaluation and not the element contained in the collection.

I don’t have frequent use for this but it occurred just a few days ago.
This is what I did then - roughly:

x = nil
enum.any? {|e| e > 10 and x = e * 2}

This is dummy code of course, I don’t have the concrete example handy.
The main point is that #any? will short circuit (like #find) and the
special value is reserved via the variable.

How about #find_map? This expresses what happens, first a search and
then a mapping of the value - sort of.

Kind regards

robert

On Thu, Nov 12, 2009 at 9:37 PM, Intransition [email protected]
wrote:

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

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 K. is accurate, sure.

2009/11/13 Martin DeMello [email protected]

On Nov 13, 5:13 am, Martin DeMello [email protected] wrote:

On Thu, Nov 12, 2009 at 9:37 PM, Intransition [email protected] wrote:

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

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.

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]

On Nov 16, 4:41 am, Brian C. [email protected] wrote:

Unless I have misunderstood, I think there is an almost identical
facility built-in:

[1,2,3].find(lambda {-1}) { |x| x > 10 }

=> -1

You have misunderstood. The desire is to do something when a match is
found, not if there is no match.

On Nov 13, 9:12 am, Benoit D. [email protected] wrote:

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

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 = [obj1, obj2].find{ |obj| obj.foo? }
y = x ? do_something_with(x.foo) : nil

vs.

y = [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.