跳过Enumerable#collect中的迭代

时间:2011-03-01 08:38:11

标签: ruby enumerable

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

如果满足next的条件,collect会将nil放入数组中,而我要做的是将 no 元素放入如果满足条件,则返回数组。如果不在返回的数组上调用delete_if { |x| x == nil },这是否可行?

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

6 个答案:

答案 0 :(得分:77)

方法Enumerable#reject仅用于此目的:

(1..4).reject{|x| x == 3}.collect{|x| x + 1}

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

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

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

(1..4).each_with_object([]){|x,a| a << x + 1 unless x == 3}

答案 1 :(得分:46)

我只需在结果数组上调用.compact,这将删除数组中的所有nil实例。如果您希望修改现有数组(无理由),请使用.compact!

(1..4).collect do |x|
  next if x == 3
  x
end.compact!

答案 2 :(得分:4)

只是一个建议,你为什么不这样做:

result = []
(1..4).each do |x|
  next if x == 3
  result << x
end
result # => [1, 2, 4]

以这种方式你保存了另一个迭代来从数组中删除nil元素。希望它有帮助=)

答案 3 :(得分:0)

我建议使用:

(1..4).to_a.delete_if {|x| x == 3}

而不是collect + next语句。

答案 4 :(得分:0)

您可以将决策制定为辅助方法,并通过Enumerable#reduce使用它:

def potentially_keep(list, i)
  if i === 3
    list
  else
    list.push i
  end
end
# => :potentially_keep

(1..4).reduce([]) { |memo, i| potentially_keep(memo, i) }
# => [1, 2, 4]

答案 5 :(得分:0)

Ruby 2.7 +

现在有!

Ruby 2.7为此引入了filter_map。它是惯用语言和高性能,我希望它很快会成为常态。

例如:

numbers = [1, 2, 5, 8, 10, 13]
enum.filter_map { |i| i * 2 if i.even? }
# => [4, 16, 20]

这里是good read on the subject

希望对某人有用!