Reputation: 5688
The Enumerable#find
method works by evaluating until the it finds an element which matches the condition in the block. Is there something similar for returning the first time that the block is not evaluated to nil? imagining one would have a collection of hashes:
value = nil
options.each do |o|
break if value = o[:desired]
end
value ||= DEFAULT
isn't there a method which already accomplishes this?
No point in making a lot of transformations to the collection, i'd like to minimize the number of allocations, so any solution which allocates a new Array will not be good for me.
Upvotes: 4
Views: 1099
Reputation: 1
How about
options.reduce{ |_,o|
break o if o[:desired]
DEFAULT
}
or
catch do |tag|
options.each{ |_,o| o[:desired] and throw tag, o }
DEFAULT
end
The latter allows for recursion.
Upvotes: 0
Reputation: 2109
This came up for me today: I think we can use break with a value from reduce
treasure = [1,2,3].reduce(nil) do |memo, value|
break memo if memo
foo(value)
end
Upvotes: 0
Reputation: 987
As of Ruby 2.0, this can be accomplished by combining #map
and #find
with lazy enumerables:
value = options.lazy.map { |o| o[:desired] }.find { |x| !x.nil? } # or find(&:present?) with ActiveSupport
value ||= DEFAULT
Upvotes: 1
Reputation: 13105
You can use reduce:
value = options.reduce(nil){|memo, entry| memo || entry[:desired] } || DEFAULT
Upvotes: 0
Reputation: 18762
find
method will work for finding first element which has :desired
key with minimum iterations.
I think you wish to get the value of desired
key from the block instead of element itself - there is no method in Enumerable
that behaves like a mixture of find
and map
- you will have to use the outer variable to which value is assigned inside the block as shown below.
options = [{foo: 1}, {desired: 2}, {bar: 3}]
value = nil
options.find do |o|
value = o[:desired]
break if value
end
p value
#=> 2
It more or less looks like your code, which should also work just fine.
Below is one way which you can use if you want to use Enumerable
methods, but it will iterate over all elements.
p value = options.map { |o| value = o[:desired] }.compact.first
Upvotes: 3