在数组中添加数字,同时避免使用nil元素

时间:2017-02-07 15:14:10

标签: ruby

我想在数组中添加数字并忽略nil元素。我的解决方案显然不起作用。

array = [3,4,5,6,nil,9,nil,9,3]

def add(array)
  array.inject(0){|memo,s|s.is_a?Integer ? memo+=s : next}
end

4 个答案:

答案 0 :(得分:7)

您的版本无法正常工作的原因很简单:块的返回值将成为下一次迭代的memo值。在s不是Integer的情况下,您只需返回任何内容,即nil,因此在下一次迭代中,memonil

您可以通过简单地返回备忘录来修复它:

def add(array)
  array.inject(0) { |memo, s| s.is_a? Integer ? memo += s : next memo }
  #                                                              ↑↑↑↑
end

这是解决问题的最小可能变化。

块通常返回块内部计算的最后一个表达式的值,因此实际上不需要next

def add(array)
  array.inject(0) { |memo, s| s.is_a? Integer ? memo += s : memo }
end

分配给memo没有任何意义,无论如何它都会超出范围的最后范围,你再也不会使用它了:

def add(array)
  array.inject(0) { |memo, s| s.is_a? Integer ? memo + s : memo }
end

根据标准的社区风格指南,消息发送的参数列表应该括在括号中,除非它是一个"类似程序的"邮件发送(例如putsrequire)或"类似关键字"一个(例如attr_accessor):

def add(array)
  array.inject(0) { |memo, s| s.is_a?(Integer) ? memo + s : memo }
end

就个人而言,我不喜欢条件运算符。这是一个风格问题,但我更喜欢这个:

def add(array)
  array.inject(0) { |memo, s| if s.is_a?(Integer) then memo + s else memo end }
end

但是,更简单的解决方案是在迭代之前删除nil元素:

def add(array)
  array.reject {|s| s.nil? }.inject(0) { |memo, s| memo + s }
end

使用Symbol#to_proc

可以更优雅地编写
def add(array)
  array.reject(&:nil?).inject(0) { |memo, s| memo + s }
end

但实际上只相当于更具可读性:

def add(array)
  array.compact.inject(0) { |memo, s| memo + s }
end

inject允许您将合并方法的名称作为Symbol传递:

def add(array)
  array.compact.inject(0, :+)
end

这与sum

相同
def add(array)
  array.compact.sum
end

答案 1 :(得分:3)

使用compact

array.compact.reduce(0,:+)

答案 2 :(得分:1)

你可以采取多种方式:

array = [3,4,5,6,nil,9,nil,9,3]
array.sum(&:to_i) # => 39
array.reject(&:nil?).sum # => 39

您可以使用compact代替reject(&:nil?)

array.compact.sum # => 39

如果您没有sum可用,如果您使用的是合理的Ruby,则可以使用inject(:+)

array.compact.inject(:+) # => 39
require 'fruity'

compare do
  sum_to_i { array.sum(&:to_i) }
  reject_nil { array.reject(&:nil?).sum }
  compact_reduce { array.compact.reduce(0,:+) }
  compact_sum { array.compact.sum }
  compact_inject { array.compact.inject(:+) }
  do_not_do_this { array.reject{|a| a.nil?}.inject(0){|memo,a| memo+=a} }
end

# >> Running each test 8192 times. Test will take about 1 second.
# >> compact_sum is faster than compact_inject by 10.000000000000009% ± 10.0%
# >> compact_inject is similar to compact_reduce
# >> compact_reduce is faster than sum_to_i by 19.999999999999996% ± 10.0%
# >> sum_to_i is faster than reject_nil by 1.9x ± 0.1
# >> reject_nil is faster than do_not_do_this by 2.0x ± 0.1

Jörg's solution显然是最快的。

答案 3 :(得分:0)

我明白了:

array.reject{|a| a.nil?}.inject(0){|memo,a| memo+=a}