¿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.

brainfck
fuente
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)

zenazn
fuente
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
jomey
fuente
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:

Evan
fuente
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
Mike Woodhouse
fuente
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
jrhicks
fuente
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í.

Eli Sadoff
fuente
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.

recoger
fuente
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}
HashFail
fuente
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, &:+) 
Vova
fuente
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(:+)
Ganesh Sagare
fuente
¿Por qué esto funciona, inject(:+)pero esto no inject :+?
Arnold Roa
@ArnoldRoa "inyectar: ​​+" funciona para mí, ¿qué resultado obtuviste?
Ganesh Sagare
6

Esto es suficiente [1,2,3].inject('+')

Mahesh Bablu
fuente
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
Santhosh
fuente
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
más asqueroso
fuente
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(:+)
los códigos de estudios
fuente
2
array.reduce(:+)

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

(1..10).reduce(:+) returns 55
MulleOne
fuente
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.

Ulysse BN
fuente
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+
Nataraja B
fuente
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('+')

Poonkodi
fuente
0

O puedes probar este método:

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

Este es el camino más corto. Intentalo.

array.inject :+

Tej Poudel
fuente
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 []

Madeline Young
fuente
-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
Prabhakar Undurthi
fuente
-8

Puede usar .map y .sum como:

array.map { |e| e }.sum
Shabdar
fuente
3
¿Para qué sirve un mapa que devuelve el mismo elemento? esto es exactamente la misma quearray.sum
Arnold Roa
Además, array.sum no existe en ruby. Ver la respuesta de Mike Woodhouse
Ulysse BN el
Lo hace ahora en Ruby 2.4.0
installero