Ilyuma Ruby for short circuit to return non-nil first, using each and a map

This is the essence of what I'm trying to do, but the “break” does not work correctly:

needle = nil
haystacks.each do |haystack|
  needle = haystack.look_for_needle()
  break if needle
end

This is shorter, but it will iterate over all the piles of hay (without looking, at least), even if it is not needed:

needle = nil
haystacks.each do |haystack|
  needle ||= haystack.look_for_needle()
end

This is single-line, but (I believe) it is not lazy and therefore does unnecessary work:

needle = hackstacks.map{|h| h.look_for_needle()}.detect{|x| !x.nil?}

I feel that there should be one liner, but I'm not sure what it will be:

needle = hackstacks.some_find_each_first_detect_lazy_map_thingee {|h| h.look_for_needle()}
+4
source share
4 answers

With Ruby 2.x lazy enumerators :

needle = haystacks.lazy.map(&:look_for_needle).reject(&:nil?).first

Or:

needle = haystacks.lazy.map(&:look_for_needle).detect { |needle| !needle.nil? }

Or:

needle = haystacks.lazy.map(&:look_for_needle).detect(&:itself)
+4
source
haystack.find &:itself
haystack.index &:itself

Which one do you prefer?

+2
source

, find_proverbial_needle_in_a_haystack look_for_needle nil, , .

class Haystack
  def initialize(haystack)
    @haystack = haystack
  end

  # Suppose look_for_needle is defined as follows
  def look_for_needle
    @haystack.include?(:straw) && :straw
  end 
end

def find_proverbial_needle_in_a_haystack(haystacks)
  needle = nil # can be anything
  haystacks.find { |haystack| needle = haystack.look_for_needle } && needle
end

find , true nil, .

haystacks = [Haystack.new([:ball, :top]),
             Haystack.new([:fluff, :straw]),
             Haystack.new([:dog, :cat])]
  #=> [#<Haystack:0x007fdaaa0f6860 @haystack=[:ball, :top]>,
  #    #<Haystack:0x007fdaaa0f67e8 @haystack=[:fluff, :straw]>,
  #    #<Haystack:0x007fdaaa0f6590 @haystack=[:dog, :cat]>] 
find_proverbial_needle_in_a_haystack(haystacks)
  #=> :straw 

haystacks = [Haystack.new([:ball, :top]),
             Haystack.new([:fluff, :yellow_stuff]),
             Haystack.new([:dog, :cat])]
  #=> [#<Haystack:0x007fdaaa082f50 @haystack=[:ball, :top]>,
  #    #<Haystack:0x007fdaaa082f00 @haystack=[:fluff, :yellow_stuff]>,
  #    #<Haystack:0x007fdaaa082eb0 @haystack=[:dog, :cat]>]     
find_proverbial_needle_in_a_haystack(haystacks)
  #=> nil
+1

There is a method firstthat does just that.

A block is required and returns the first block match

match = haystacks.first { |x| x.find_needle }

https://ruby-doc.org/core-2.2.0/Array.html#method-i-first

By the way, you can use eachor mapwithout extra keystrokes:

match = haystacks.each { |x| break x if x.find_needle }
match = haystacks.map { |x| break x if x.find_needle }

although it doesn't actually use the correct semantics eachor mapthat you expect to return an array ( eachreturns the original array, but mapreturns the converted array)

0
source

Source: https://habr.com/ru/post/1652916/


All Articles