如何在Ruby中求和数字数组?


563

我有一个整数数组。

例如:

array = [123,321,12389]

有没有什么好办法来得到它们的总和?

我知道

sum = 0
array.each { |a| sum+=a }

会工作。


19
请注意,Ruby 2.4+具有array.sum
dawg

Ruby 2.6没有它。看起来,Ruby付出了,Ruby放弃了。
罗里

1
@Lori嗯?链接
steenslag

抱歉。当时我错误地认为我正在使用2.6,因为我的rbenv滑倒了。
罗里

Answers:


612

尝试这个:

array.inject(0){|sum,x| sum + x }

请参阅Ruby的可枚举文档

(请注意:0需要基本情况​​,以便0将其返回到一个空数组而不是nil


317
乔尼的array.inject(:+)效率更高。
彼得

3
array.inject(:+)似乎在Ruby 1.8.6中引起麻烦。可能会弹出“ LocalJumpError:未提供任何块”。
卡米尔·索佐特

34
in rails array.sum可能会给您数组值的总和。
卡米尔·索佐特

32
在大多数情况下,我更喜欢使用reduce,这是的别名inject(如array.reduce( :+ ))。
Boris Stitnicky

3
@Boris另外,Rubycop将警告您使用inject而不是reduce
Droogans

810

或者尝试使用Ruby 1.9:

array.inject(0, :+)

注意:0需要基本情况​​,否则nil将在空数组上返回:

> [].inject(:+)
nil
> [].inject(0, :+)
0

6
如何使用这种方式从对象求和属性。我的数组[product1,product2]我想对product1.price + product2.price求和。是否可以使用array.inject(:+)?
Pablo Cantero

7
您可以对地图方法使用类似的技巧:array.map(&:price).inject(:+)
markquezada 2011年

100
array.map(&:price).inject(0, :+)比较安全。它确保如果您有一个空列表,则得到0而不是nil
约翰·约翰逊2011年

11
使用array.map(...)。inject(...)效率低下,您将遍历所有数据两次。试试 array.inject(0) { |sum, product| sum += product.price }
everett1992

4
@ everett1992,事实证明,甚至根本没有优化。对我而言,分两个阶段进行操作总是更快。gist.github.com/cameron-martin/b907ec43a9d8b9303bdc
卡梅隆·马丁

290
array.reduce(0, :+)

虽然等同array.inject(0, :+),但随着MapReduce编程模型的兴起,减少一词进入了一个更为普遍的说法。

注入缩小折叠累积压缩都是一类折叠函数的同义词。我发现在您的代码库中保持一致性是最重要的,但是由于各个社区倾向于优先使用一个单词而不是另一个单词,因此了解替代方法还是很有用的。

为了强调map-reduce语言,这里提供了一个版本,该版本对该数组中的最终内容略加宽容。

array.map(&:to_i).reduce(0, :+)

一些其他相关阅读:


11
我同意,reduce可以进一步告诉我该功能的作用,但inject听起来确实要酷得多。
everett1992

1
同意最后的评论,您给了我最佳答案。
耶尔斯卡

1
在一个评论我要提出的是,reducemap更高阶的功能早的MapReduce。灵感是另一种方式。在MapReduce的意义上,它与简单的功能缩减有所不同,对不同机器的通信方式有影响。
acjay 2015年

肯·艾弗森(Ken Iverson)用编程语言APL引入了运算符/称为“归约运算符”。资料来源:艾弗森,肯尼斯。1962年。一种编程语言。威利。另一个来源:“符号作为思想工具”,1979年ACM图灵奖演讲,肯尼思·艾弗森(Kenneth E. Iverson),dl.acm.org / ft_gateway.cfm?id = 1283935&type = pdf
Fernando Pelliccioni,

112

或者(仅作比较),如果您安装了Rails(实际上只是ActiveSupport):

require 'activesupport'
array.sum

12
默认情况下,较新版本的activesupport实际上不会加载所有扩展。您可能只需要sum模块:require 'active_support/core_ext/enumerable.rb',或者需要所有活动的支持:require 'active_support/all'。有关此信息的更多信息,请访问:API Docs
dcashman 2012年

2
不要介意,activesupport是一个巨大的依赖关系拖入一个项目,从去array.inject(:+)array.sum
meagar

1
Nitpick给出了一个很好的评论:它应该require 'active_support/core_ext/enumerable'没有.rb后缀,因为它是隐式添加的。
Per Lundberg

72

对于Ruby> = 2.4.0,可以使用sumEnumerables。

[1, 2, 3, 4].sum

mokeypatch基类很危险。如果您喜欢危险并使用旧版本的Ruby,可以将其添加#sumArray该类中:

class Array
  def sum
    inject(0) { |sum, x| sum + x }
  end
end

1
请不要这样做
user3467349 '16

@ user3467349为什么?
YoTengoUnLCD

15
Monkeypatching基类不是很好。
user3467349

1
他的意思是,您不需要为Ruby> = 2.4进行Monkey Patch,并且猴子修补很危险,现在您可以在本地对可枚举对象求和,但是也有一种方法可以反向移植功能。
Peter H. Boling

投票不足,因为您的实现在空数组上返回nil。
Eldritch难题,2017年

45

Ruby 2.4.0的新功能

您可以使用适当命名的方法Enumerable#sum。与之相比,它具有很多优点,inject(:+)但最后还要阅读一些重要说明。

例子

范围

(1..100).sum
#=> 5050

数组

[1, 2, 4, 9, 2, 3].sum
#=> 21

[1.9, 6.3, 20.3, 49.2].sum
#=> 77.7

重要的提示

此方法不等同于#inject(:+)。例如

%w(a b c).inject(:+)
#=> "abc"
%w(a b c).sum
#=> TypeError: String can't be coerced into Integer

也,

(1..1000000000).sum
#=> 500000000500000000 (execution time: less than 1s)
(1..1000000000).inject(:+)
#=> 500000000500000000 (execution time: upwards of a minute)

有关原因的更多信息,请参见此答案sum


19

仅出于多样性的考虑,如果您的数组不是数字数组,而是具有数字(例如数量)属性的对象数组,也可以执行此操作:

array.inject(0){|sum,x| sum + x.amount}

3
这等效于:array.map(&:amount).inject(0, :+)。查看其他答案。
理查德·琼斯

4
在某种程度上,是的。但是,使用map then inject要求您循环遍历两次数组:一次创建一个新数组,另一次对成员求和。此方法稍微冗长一些,但也更有效。
HashFail 2014年


19

Ruby 2.4+ / Rails- array.sum[1, 2, 3].sum # => 6

Ruby pre 2.4- array.inject(:+)array.reduce(:+)

*注意:该#sum方法是2.4的新增功能,enumerable因此您现在可以使用array.sum纯红宝石,而不仅仅是Rails。


2
Ruby 2.4.0现已发布,其中包含此功能!🎉–
变形虫

@amoebe你是正确的!很高兴看到其中包含此有用的功能。
收集

18

红宝石1.8.7的方式如下:

array.inject(0, &:+) 

如果您阅读了我的2011年评论,但在使用1.8.6时仍然有用,请升级!
Andrew Grimm 2014年



5

Ruby 2.4.0已发布,它具有Enumerable#sum方法。所以你可以做

array.sum

来自文档的示例:

{ 1 => 10, 2 => 20 }.sum {|k, v| k * v }  #=> 50
(1..10).sum                               #=> 55
(1..10).sum {|v| v * 2 }                  #=> 110

3

还允许[1,2].sum{|x| x * 2 } == 6

# http://madeofcode.com/posts/74-ruby-core-extension-array-sum
class Array
  def sum(method = nil, &block)
    if block_given?
      raise ArgumentError, "You cannot pass a block and a method!" if method
      inject(0) { |sum, i| sum + yield(i) }
    elsif method
      inject(0) { |sum, i| sum + i.send(method) }
    else
      inject(0) { |sum, i| sum + i }
    end
  end
end

3

对于nil值的数组,我们可以进行压缩,然后注入和

a = [1,2,3,4,5,12,23.45,nil,23,nil]
puts a.compact.inject(:+)

2
array.reduce(:+)

也适用于范围 ...因此,

(1..10).reduce(:+) returns 55

1

如果您觉得高尔夫,可以

eval([123,321,12389]*?+)

这将创建一个字符串“ 123 + 321 + 12389”,然后使用函数eval进行求和。这为了打高尔夫球,请勿在正确的代码中使用它。


1

方法1:

    [1] pry(main)> [1,2,3,4].sum
    => 10
    [2] pry(main)> [].sum
    => 0
    [3] pry(main)> [1,2,3,5,nil].sum
    TypeError: nil can't be coerced into Integer

方法2:

   [24] pry(main)> [].inject(:+)
   => nil
   [25] pry(main)> [].inject(0, :+)
   => 0
   [4] pry(main)> [1,2,3,4,5].inject(0, :+)
   => 15
   [5] pry(main)> [1,2,3,4,nil].inject(0, :+)
   TypeError: nil can't be coerced into Integer
   from (pry):5:in `+'

方法3:

   [6] pry(main)> [1,2,3].reduce(:+)
   => 6
   [9] pry(main)> [].reduce(:+)
   => nil
   [7] pry(main)> [1,2,nil].reduce(:+)
   TypeError: nil can't be coerced into Integer
   from (pry):7:in `+'

方法4: 当Array包含nil和空值时,默认情况下,如果您使用上述任何函数reduce,sum,inject都会通过

TypeError:无法将nil强制转换为Integer

你可以克服这个

   [16] pry(main)> sum = 0 
   => 0
   [17] pry(main)> [1,2,3,4,nil, ''].each{|a| sum+= a.to_i }
   => [1, 2, 3, 4, nil, ""]
   [18] pry(main)> sum
   => 10

方法6: 评估

计算字符串中的Ruby表达式。

  [26] pry(main)> a = [1,3,4,5]
  => [1, 3, 4, 5]
  [27] pry(main)> eval a.join '+'
  => 13
  [30] pry(main)> a = [1,3,4,5, nil]
  => [1, 3, 4, 5, nil]
  [31] pry(main)> eval a.join '+'
  SyntaxError: (eval):1: syntax error, unexpected end-of-input
  1+3+4+5+

1

我们可以对数组求和的3种方法

1) array.inject(0){|sum,x| sum + x }

2) array.inject('+')

3) array.join('+')


0

或者您可以尝试以下方法:

def sum arr
  0 if arr.empty
  arr.inject :+
end


0
number = [1..100]

number. each do |n|

    final_number = n.sum

    puts "The sum is #{final_number}"
end

*这对我作为新开发人员来说非常有效。您可以通过更改[]中的值来调整数字范围


-1

您也可以轻松地做到这一点

def sum(numbers)
  return 0 if numbers.length < 1
  result = 0
  numbers.each { |num| result += num }
  result
end

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.