我有一个对象数组。我需要找到满足某些条件的第一个并返回它的转换版本。通常,此操作可以命名为“map-detect”。
input = [2, 3, 4]
mapper = ->(v) { result = v * v; result if result.odd? }
detector = ->(v) { v } # &itself
expected_output = 9
解决方案是懒惰映射所有内容然后detect
:
input.lazy.map(&mapper).detect(&detector)
#⇒ 9
这看起来有点多余;我需要的是通过一个Enumerable
来映射它,检测第一个真相并返回它。其他方式是:
input.each do |v|
mapped = mapper.(v)
break mapped if detector.(mapped)
end
这看起来像是滥用each
。所以,我的问题是:在ruby中有map-detect
的原生方式吗?
答案 0 :(得分:2)
如评论中所述,纯粹是为了检测并返回第一个非nil
映射值,这有效:
die "Module refinements need 2.4" if RUBY_VERSION < "2.4.0"
module MapDetectFirstRefinement
refine Enumerable do
def map_detect_first
each_with_object(nil) do |e, _|
mapped = yield e
return mapped if mapped
end
end
end
end
module TestMapDetectFirst
using MapDetectFirstRefinement
puts [2,3,4].map_detect_first { |v| result = v * v; result if result.odd? }
end
答案 1 :(得分:1)
如果mapper
是一项昂贵的操作,我可以理解你为什么要这样做。我认为问题源于map
(带有一个块)返回一个数组而不是一个枚举器的事实。一种解决方案是简单地创建一个执行map
工作的枚举器。
def doit(input, mapper, detector)
Enumerator.new do |y|
input.each do |e|
y << mapper.(e)
end
end.find { |m| detector.(m) }
end
array = [2, 3, 4]
mapper = ->(v) { result = v * v; result if result.odd? }
doit(array, mapper, ->(v) { v }) #=> 9
doit(array, mapper, ->(v) { nil }) #=> nil
还有其他原因可能导致人们不希望将mapper
应用于数组的所有元素(除了它是一项昂贵的操作)。假设
detector = ->(v) { v }
array = [2, 3, 4, 5, "cat"]
和mapper
如上所述(轰炸&#34; cat&#34;)或
->(v) { |v| v == 5 ? launch_missiles(); v * v if v.odd? }
答案 2 :(得分:1)
感谢@Amadan的启发性评论:我们可以缩小映射器的强制条件(如果输入无效则返回nil
)然后使用哑检测器,允许唯一的块到辅助方法:
def map_detect(input)
return input.enum_for(:each_with_object, nil) unless block_given?
input.each_with_object(nil) do |value, _|
result = yield value
break result if result
end
end
答案 3 :(得分:0)
我不知道我的问题是否正确,但是如果你只是需要一种方法来找到ruby数组中的第一个匹配并用它做一些事情,你可以简单地做一些事情:
my_array = [1,2,3,4,5]
# creates a new array for each element in my_array where the given block returns true
matches = my_array.select {|entry| what_ever_the_condition_is }
# if there is a match
if !matches.empty?
# get the first entry
first = matches.first
# do something with the match
# ....
# ....
end