跳过Enumerable#collect中的迭代

And*_*all 54 ruby enumerable

(1..4).collect do |x|
  next if x == 3
  x + 1
end # => [2, 3, nil, 5]
    # desired => [2, 3, 5]
Run Code Online (Sandbox Code Playgroud)

如果满足条件,nextcollect放入nil数组,而我正在尝试做的是,如果满足条件,则不在返回的数组中放置任何元素.这可能不调用delete_if { |x| x == nil }返回的数组吗?

(使用Ruby 1.8.7;我的代码摘录被大量抽象)

Mla*_*vić 78

有一种方法Enumerable#reject可以达到目的:

(1..4).reject{|x| x == 3}.collect{|x| x + 1}
Run Code Online (Sandbox Code Playgroud)

直接使用一个方法的输出作为另一个方法的输入的做法称为方法链接,在Ruby中很常见.

BTW,map(或collect)用于将可枚举的输入直接映射到输出的输入.如果您需要输出不同数量的元素,那么您可能需要另一种方法Enumerable.

编辑:如果您对某些元素被迭代两次这一事实感到困扰,您可以使用基于inject(或其类似方法命名each_with_object)的不太优雅的解决方案:

(1..4).each_with_object([]){|x,a| a << x + 1 unless x == 3}
Run Code Online (Sandbox Code Playgroud)

  • 这是最慢的答案(虽然不是很多),但肯定是最干净的.我只是希望有一种方法可以用`collect`来做到这一点,因为我希望调用`next`返回_绝对没有_,而不是"没有"(又名"nil"). (4认同)
  • 美丽的代码.这比我的解决方案要好得多. (3认同)

Ben*_*son 46

我只是调用.compact结果数组,它删除数组中的任何nil实例.如果您想要修改现有阵列(没有理由不这样做),请使用.compact!:

(1..4).collect do |x|
  next if x == 3
  x
end.compact!
Run Code Online (Sandbox Code Playgroud)

  • 为了兴趣,我为目前建议的四种解决方案做了快速基准:收集+紧凑,收集+紧凑!,拒绝+收集并构建结果数组.在MRI 1.9.1中,至少,收集+紧凑!是一个狭窄的速度赢家,收集+紧凑和拒绝+收集非常接近.构建结果数组的速度大约是那些数组的两倍. (13认同)
  • 这可行,但你必须担心原始数组中有合法的"nil"的情况. (6认同)
  • 我不知道如何使用`map {}.compact!`,b/c如果没有跳过则返回`nil`.我想如果可以保证至少会跳过一个元素...... (3认同)

SRa*_*ack 6

红宝石 2.7+

现在有!

Ruby 2.7 正是filter_map为此目的而引入的。这是惯用的和高性能的,我希望它很快成为常态。

例如:

numbers = [1, 2, 5, 8, 10, 13]
numbers.filter_map { |i| i * 2 if i.even? }
# => [4, 16, 20]
Run Code Online (Sandbox Code Playgroud)

这是有关该主题好读物

希望这对某人有用!