¿Cómo sumar una matriz de números en Ruby?


564

Tengo una serie de enteros.

Por ejemplo:

array = [123,321,12389]

¿Hay alguna forma agradable de obtener la suma de ellos?

Yo sé eso

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

trabajaría.


19
Tenga en cuenta que Ruby 2.4+ tienearray.sum
dawg

Ruby 2.6 no lo tiene. Ruby da, Ruby quita, parece.
Lori el

1
@Lori hmm? enlace
steenslag

Lo siento. En ese momento, creí erróneamente que estaba usando 2.6 debido a un error de rbenv de mi parte.
Lori

Respuestas:


613

Prueba esto:

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

Ver la documentación enumerable de Ruby

(nota: el 0caso base es necesario para que 0se devuelva en una matriz vacía en lugar de nil)


317
Jordan array.inject(:+)es más eficiente.
Peter

3
array.inject(:+)parece causar problemas en Ruby 1.8.6. Pueden aparecer excepciones "LocalJumpError: sin bloque".
Kamil Szot

34
En rails array.sumpuede darte la suma de los valores de la matriz.
Kamil Szot

32
En la mayoría de los casos, prefiero usar reduce, que es un alias de inject(como en array.reduce( :+ )).
Boris Stitnicky

3
@Boris Además, Rubycop te advertirá por usar en injectlugar de reduce.
Droogans

810

O prueba la forma Ruby 1.9:

array.inject(0, :+)

Nota: el 0caso base es necesario; de lo contrario nil, se devolverá en matrices vacías:

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

66
¿Cómo puedo usar esta forma de sumar un atributo del objeto? Mi matriz [product1, product2] Quiero sumar product1.price + product2.price. ¿Es posible usar array.inject (: +)?
Pablo Cantero

77
Puede usar un truco similar con el método de mapa: array.map (&: price) .inject (: +)
markquezada

100
array.map(&:price).inject(0, :+)Es un poco más seguro. Se asegura de que si tiene una lista vacía, obtenga 0 en lugar de cero .
johnf

11
el uso de array.map (...). inyectar (...) es ineficiente, iterará a través de todos los datos dos veces. Prueba array.inject(0) { |sum, product| sum += product.price }
everett1992

44
@ everett1992 y, como resultado, ni siquiera una optimización en absoluto. Hacerlo en dos etapas es consistentemente más rápido para mí. gist.github.com/cameron-martin/b907ec43a9d8b9303bdc
Cameron Martin

290
array.reduce(0, :+)

Si bien es equivalente a array.inject(0, :+), el término reducir está entrando en una lengua vernácula más común con el surgimiento de los modelos de programación MapReduce .

inyectar , reducir , plegar , acumular y comprimir son sinónimos de una clase de funciones de plegado . Considero que la coherencia en su código base es lo más importante, pero dado que varias comunidades tienden a preferir una palabra sobre otra, de todos modos es útil conocer las alternativas.

Para enfatizar el lenguaje de reducción de mapas, aquí hay una versión que es un poco más indulgente con lo que termina en esa matriz.

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

Algunas lecturas relevantes adicionales:


11
Estoy de acuerdo, reduceme dice más de lo que hace la función, pero injectsuena mucho mejor.
everett1992

1
De acuerdo con el último comentario, me diste la mejor respuesta.
Jerska

1
El único comentario que haría es eso reducey mapcomo funciones de orden superior son anteriores a MapReduce. La inspiración va para otro lado. Y en el sentido de MapReduce, es una operación algo diferente a una simple reducción funcional, que tiene implicaciones sobre cómo se comunican las diferentes máquinas.
acjay

Ken Iverson introdujo el operador / llamado "operador de reducción" en el lenguaje de programación APL. Fuente: Iverson, Kenneth. 1962. Un lenguaje de programación. Wiley Otra fuente: "Notación como herramienta de pensamiento", Conferencia del Premio ACM Turing 1979, Kenneth E. Iverson, dl.acm.org/ft_gateway.cfm?id=1283935&type=pdf
Fernando Pelliccioni

112

Alternativamente (solo para comparación), si tiene Rails instalado (en realidad solo ActiveSupport):

require 'activesupport'
array.sum

12
Las versiones más recientes de activeiveupport en realidad no cargan todas las extensiones de forma predeterminada. Usted querrá tampoco requieren sólo la suma módulo: require 'active_support/core_ext/enumerable.rb', o exigir a las de apoyo activo: require 'active_support/all'. Más sobre esto aquí: API Docs
dcashman

2
No importa que activesupportsea ​​una dependencia masiva para arrastrar a un proyecto para ir array.inject(:+)a array.sum.
Meagar

1
Nitpick a un comentario bueno: debería estar require 'active_support/core_ext/enumerable'sin el .rbsufijo, ya que se agrega implícitamente.
Según Lundberg el

72

Para Ruby> = 2.4.0 puede usar sumde Enumerables.

[1, 2, 3, 4].sum

Es peligroso para las clases base mokeypatch. Si le gusta el peligro y utiliza una versión anterior de Ruby, puede agregar #suma la Arrayclase:

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

1
Por favor no hagas esto
user3467349

@ user3467349 ¿por qué?
YoTengoUnLCD

15
Monkeypatching clases base no es bueno.
user3467349

1
El punto que está diciendo es que no necesita hacer el parche de mono para Ruby> = 2.4, y que el parche de mono es peligroso, y que ahora puede sumar enumerables de forma nativa, pero también hay una manera de respaldar la funcionalidad.
Peter H. Boling

Voto negativo porque su implementación devuelve nulo en matrices vacías.
Eldritch Conundrum

45

Nuevo para Ruby 2.4.0

Puede usar el método acertadamente nombrado Enumerable#sum. Tiene muchas ventajas, inject(:+)pero también hay algunas notas importantes para leer al final.

Ejemplos

Rangos

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

Matrices

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

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

Nota IMPORTANTE

Este método no es equivalente a #inject(:+). Por ejemplo

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

También,

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

Consulte esta respuesta para obtener más información sobre por qué sumes así.


20

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

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

* Nota: El #summétodo es una nueva adición a 2.4, por enumerablelo que ahora podrá usarlo array.sumen rubí puro, no solo en Rails.


2
Ruby 2.4.0 fue lanzado hoy con esta característica incluida. Amo
amoebe

@amoebe tienes razón! Me alegra ver esta característica útil incluida.
recoge el

19

Solo por el bien de la diversidad, también puede hacer esto si su matriz no es una matriz de números, sino una matriz de objetos que tienen propiedades que son números (por ejemplo, cantidad):

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

3
Esto es equivalente a hacer: array.map(&:amount).inject(0, :+). Ver otras respuestas.
Richard Jones

44
En cierto modo, sí. Sin embargo, utilizando mapa continuación injectrequiere que recorrer la matriz dos veces: una vez para crear una nueva matriz, el otro para resumir los miembros. Este método es un poco más detallado, pero también más eficiente.
HashFail

Aparentemente no es más eficiente, vea gist.github.com/cameron-martin/b907ec43a9d8b9303bdc - crédito a los comentarios en esta respuesta: stackoverflow.com/a/1538949/1028679
rmcsharry

18

ruby 1.8.7 way es el siguiente:

array.inject(0, &:+) 

Si leyó mi comentario de 2011 y sigue siendo relevante mientras usa 1.8.6, ¡actualice!
Andrew Grimm

16

Simplemente puede usar:

    example = [1,2,3]
    example.inject(:+)

¿Por qué esto funciona, inject(:+)pero esto no inject :+?
Arnold Roa

@ArnoldRoa "inyectar: ​​+" funciona para mí, ¿qué resultado obtuviste?
Ganesh Sagare


5

Se lanza Ruby 2.4.0 y tiene un método Enumerable # sum . Entonces puedes hacer

array.sum

Ejemplos de los documentos:

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

3

También permite [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

para una matriz con valores nulos podemos hacer compactos y luego inyectar la suma ex

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

2
array.reduce(:+)

Funciona también para rangos ... por lo tanto,

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

1

Si te sientes golfista, puedes hacer

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

Esto creará una cadena "123 + 321 + 12389" y luego usará la función eval para hacer la suma. Esto es solo para fines de golf , no debe usarlo en el código adecuado.


1

Método 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

Método 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 `+'

Método 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 `+'

Método 4: cuando la matriz contiene valores nulos y vacíos, de manera predeterminada si utiliza alguna de las funciones anteriores, reduzca, sume, inyecte todo a través del

TypeError: nil no se puede convertir en Integer

Puedes superar esto,

   [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

Método 6: eval

Evalúa las expresiones de Ruby en cadena.

  [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 formas en que podemos hacer la suma de la matriz

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

2) array.inject('+')

3) array.join('+')


0

O puedes probar este método:

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

* Esto funcionó bien para mí como nuevo desarrollador. Puede ajustar su rango de números cambiando los valores dentro de []


-1

También puedes hacerlo de manera fácil

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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.