2011-03-01 156 views
51
(1..4).collect do |x| 
    next if x == 3 
    x + 1 
end # => [2, 3, nil, 5] 
    # desired => [2, 3, 5] 

如果next条件满足,collect提出nil在阵列中,而我想要做的就是把没有元素返回数组中如果条件满足。这可能不会在返回的数组上调用delete_if { |x| x == nil }跳过迭代收集

(使用Ruby 1.8.7;我的代码摘录在很大程度上抽象)

回答

74

有方法Enumerable#reject供应只是目的:

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

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

顺便说一句,map(或collect)用于输入enumerable直接映射到输出。如果您需要输出不同数量的元素,则可能需要使用另一种方法Enumerable

编辑:如果你是一个事实,即某些元素被重复两次困扰,您可以使用基于较少优雅的解决方案inject(或其类似方法命名each_with_object):

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

美丽的代码。这比我的解决方案好得多。 – Benson 2011-03-01 09:23:35

+3

这是最慢的答案(虽然不是太多),但肯定是最干净的。我只是希望有一种方法可以用'collect'做到这一点,因为我期望调用'next'返回_absolutely nothing_,而不是“nothing”(又名'nil')。 – 2011-03-01 23:12:54

+1

如果比其他任何方法都慢,那么Ruby的优化器可能会使用一些您认为没有用的工作? – Shannon 2012-12-27 21:30:00

44

我会简单地调用.compact所得阵列,这消除零的任何实例中的阵列上。如果你想它修改现有的阵列(没有理由不),使用.compact!

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

我做了一个快速基准,利息,这四个解决方案的建议至今:收集+压缩,收集+紧凑!拒绝+收集并将结果数组构建为y你去吧。在MRI 1.9.1中,至少收集+紧凑!是一个狭窄的优势,收集+紧凑和拒绝+收集在后面很接近。建立结果数组的速度大约是那些速度的两倍。 – 2011-03-01 14:01:18

+0

@glenn:你介意在你的基准测试中包括以下内容:'(1..4).inject([]){| a,x | x == 3? a:a.push(x + 1)}?谢谢。 – 2011-03-01 14:59:51

+0

当然。这是最慢的一个,虽然只比构建结果数组稍慢一点。我还添加了一个像这样调整的版本:'a.inject([]){| aa,x | aa << x + 1,除非x == 3; aa}',这比构建阵列要快,但仍然比三种快速方法慢得多。 – 2011-03-01 18:07:55

0

我会建议使用:

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

,而不是收集+下一条语句。

+0

_real_代码比我的问题中的抽象代码更复杂,所以这不会实现我正在寻找的东西,不幸的是。 (该收集实际上操纵的价值,而不是仅仅返回它) – 2011-03-01 08:53:39

+0

好吧,所以建议的契约!解决方案就是你的选择。 – ALoR 2011-03-01 08:55:36

4

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

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

以这种方式保存的另一次迭代,除去从零数组元素。希望它有助于=)

+0

这就像Mladen的“拒绝”示例的更详细的版本。 – Benson 2011-03-01 09:22:16

+0

实际上是nope,“reject”一次遍历数组,并且“collect”再次遍历数组,因此有两个迭代。在“每个”的情况下,它只做一次=) – Staelen 2011-03-01 09:27:27

+4

这是一个合理的想法,但在Ruby中,进行速度测试并查看实际发生的事情通常非常有趣。在我的快速基准测试中(当然这可能与安德鲁的实际情况相匹配),以这种方式构建阵列的速度实际上是其他任何方式的两倍。我怀疑问题是在C中迭代数组实际上要比在Ruby <<级别追加项目要快得多。 – 2011-03-01 14:04:28

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]