Ruby Detect Enumerable (AKA Ruby Find enumerable)
I love Ruby’s enumerable methods.
Enumerable#Detect is probably one of the least well known, and thus most people never use it.
It’s the exact same thing as Enumerable#Find. It’s actually stupid simple.
Of course, it’s not as glamorous as superstars like map or select, but as I explain below, it’s a nice way to remove an additional method call.
How to use it
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# Theory [1,2,3].detect{|i| i == 1} #returns the first element for which the block returns true #Practice class Person; attr_accessor :special; end p1 = Person.new; p2 = Person.new; p3 = Person.new p1.special = p2.special = false p3.special = true [p1,p2,p3].detect{|p| p.special} === p3 #=> true # Ruby doc (1..10).detect {|i| i % 5 == 0 and i % 7 == 0 } #=> nil (1..100).detect {|i| i % 5 == 0 and i % 7 == 0 } #=> 35 # Replacement for... >> [p1,p2,p3].select{|p| p.special}.first is the same as >> [p1,p2,p3].detect{|p| p.special} |
Real life use:
1 |
thumb = attachment.thumbnails.detect { |t| t.filename =~ /_thumb/ }
|
1 |
finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
|
Implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
## # :call-seq: # enum.detect(ifnone = nil) { | obj | block } => obj or nil # enum.find(ifnone = nil) { | obj | block } => obj or nil # # Passes each entry in +enum+ to +block+>. Returns the first for which # +block+ is not false. If no object matches, calls +ifnone+ and returns # its result when it is specified, or returns nil # # (1..10).detect { |i| i % 5 == 0 and i % 7 == 0 } #=> nil # (1..100).detect { |i| i % 5 == 0 and i % 7 == 0 } #=> 35 def find(ifnone = nil) each { |o| return o if yield(o) } ifnone.call if ifnone end alias_method :detect, :find |
Dead simple.
Pro Tip
You may notice from the Rubinius source that detect will call any method that you pass in as an argument if the block doesn’t return true for any of the elements.
This lets us do fun stuff like:
1 2 3 |
>> [7,1,4].detect(lambda{puts 'Oops'}){|number| number == 42} Oops => nil |
And of course, we could even return something.
1 2 |
>> [7,1,4].detect(lambda{return 'Oops'}){|number| number == 42} => "Oops" |
Interesting. That’s all for now though.


Recent comments
1 year 23 weeks ago
1 year 23 weeks ago
1 year 25 weeks ago
1 year 27 weeks ago
1 year 42 weeks ago
1 year 45 weeks ago
1 year 45 weeks ago
1 year 45 weeks ago
1 year 46 weeks ago
1 year 48 weeks ago