在Ruby中,遍历一个数组并在一行中初始化多个变量



我正在尝试对数组进行迭代,并计算数组中的正、负和零的数量。现在我像这个一样做

arr = [1, -1, 0, 2, 3, -2, -5]
pos = arr.select { |i| i > 0 }.count
neg = arr.select { |i| i < 0 }.count
zero = arr.select { |i| i == 0 }.count
puts pos
puts neg
puts zero

但是,有什么办法可以让我在一行中做到这一点吗?像这样的东西?

pos, neg, zero = arr.select { |i| i > 0; i < 0; i == 0; }.count

使用inject<=>运算符:

neg, zero, pos = arr.inject([0,0,0]) { |a,b| a[(b<=>0)+1] += 1; a }

或者,正如@Holger刚才提到的:

neg, zero, pos = arr.each_with_object([0,0,0]) { |a,b| b[(a<=>0)+1] += 1 }

稍长,但在块中没有额外的CCD_ 3。


受@steenslag使用tally:的启发

neg, zero, pos = arr.map { |x| x<=>0 }.tally.values_at(-1,0,1)

如果使用计数哈希,代码很短,结果以哈希形式返回,这可能很方便。

arr = [1, -1, 0, 2, 3, -2, -5, 4]

你可以写

arr.each_with_object(Hash.new(0)) { |n,h| h[n<=>0] += 1 }
#=> {1=>4, -1=>3, 0=>1}

或者你可能更喜欢

labels = { -1=>:neg, 0=>:zero, 1=>:pos }
arr.each_with_object(Hash.new(0)) { |n,h| h[labels[n<=>0]] += 1 }
#=> {:pos=>4, :neg=>3, :zero=>1}

其最后一行也可以写入

arr.each_with_object({}) { |n,h| h[labels[n<=>0]] = (h[labels[n<=>0]] ||= 0) + 1 }

请参阅Hash::new,特别是(第二种(形式,它采用一个名为默认值(此处为零(的参数,并且没有块。如果散列被定义为h = Hash.new(0),那么如果h没有密钥k,则h[k]返回0(并且h不改变(。

arr = [1, -1, 0, 2, 3, -2, -5]
neg, zero, pos = arr.map{|n| n <=> 0}.tally.values_at(-1, 0, 1)

使用新的计数方法。

正如其他人已经说过的,您应该只使用inject并使用<>操作人员如果你计划经常使用类似的逻辑,你可以把#tally_by方法猴子补丁到Enumerable中,比如:

class Enumerable
def my_tally(*keys, &proc)
proc ||= -> e {e}    # Default identity proc
h = keys.empty? ? Hash.new(0) : Hash[keys.map{|k|[k, 0]}]
inject(h){|a, e| a[proc.call(e)] += 1; a}
end
end

这允许你写:

neg, zero, pos = arr.my_tally(-1, 0, 1){|e| e <=> 0}

虽然这肯定是比其他代码更先进的代码,但如果您发现自己经常使用类似的逻辑,那么拥有它可能会很好。如果你不喜欢猴子补丁,你也可以在某个地方把它作为一种常规方法。

最新更新