Ruby - Array.find, but return the value the block

2020-08-10 07:36发布

I have an array, and I want the result of the first block that returns a truthy value (aka, not nil). The catch is that in my actual use case, the test has a side effect (I'm actually iterating over a set of queues, and pop'ing off the top), so I need to not evaluate the block beyond that first success.

a,b,c = [1,2,3]

[a,b,c].first_but_value{ |i| (i + 1) == 2 } == 2

a == 2
b == 2
c == 3

Any ideas?

标签: ruby
6条回答
虎瘦雄心在
2楼-- · 2020-08-10 07:52
[1, 2, 3].detect { |i| i += 1; break i if i == 2 } 
# => 2

[1, 2, 3].detect { |i| i += 1; break i if i == 10 }
# => nil
查看更多
我想做一个坏孩纸
3楼-- · 2020-08-10 07:56

break is ugly =P

If you want a functional approach, you want a lazy map:

[nil, 1, 2, 3].lazy.map{|i| i && i.to_s}.find &:itself   
# => "1"

If you don't believe it is not iterating throughout the array, just print out and see:

[nil, 1, 2, 3].lazy.map{|i| (p i) && i.to_s}.find  &:itself
# nil
# 1
# => "1"

Replace i.to_s by your block.

查看更多
男人必须洒脱
4楼-- · 2020-08-10 07:56

I doubt there's a way to do this. The problem being that Ruby creates a closure in the block and the variable i is local to it. Doing i+=1 can be expanded to i = i + 1 which creates a new variable i in the block's scope and doesn't modify the value in any of your a,b,c variables.

查看更多
霸刀☆藐视天下
5楼-- · 2020-08-10 07:57

Here's my take, is this closer to your actual use case? Note the content of b is 3 instead of 2 because my_test_with_side_effect is called on b as well.

class MyQueue
  def initialize(j)
    @j = j
  end  
  def my_test_with_side_effect
    (@j+=1) == 2
  end
end

(a,b,c) = [MyQueue.new(1),MyQueue.new(2),MyQueue.new(3)]
[a,b,c].each { |i| break i unless i.my_test_with_side_effect }
=> #<MyQueue:0x007f3a8c693598 @j=3>
a
=> #<MyQueue:0x007f3a8c693980 @j=2>
b
=> #<MyQueue:0x007f3a8c693598 @i=3>
c
=> #<MyQueue:0x007f3a8c693430 @i=3>
查看更多
冷血范
6楼-- · 2020-08-10 08:06

find_yield does what you want, check out ruby facets with many core extensions, and especially find_yield Enumberable method: https://github.com/rubyworks/facets/blob/master/lib/core/facets/enumerable/find_yield.rb

查看更多
一纸荒年 Trace。
7楼-- · 2020-08-10 08:08

Is this what you want to do?

a, b, c = 1, 2, 3

binding.tap { |b|
  break b.local_variable_get [ :a, :b, :c ].find { |sym|
    b.local_variable_set( sym, b.local_variable_get( sym ) + 1 ) == 2
  }
} #=> 2

a #=> 2
b #=> 2
c #=> 3
查看更多
登录 后发表回答