Como somar matriz de números em Ruby?

563

Eu tenho uma matriz de números inteiros.

Por exemplo:

array = [123,321,12389]

Existe alguma maneira legal de obter a soma deles?

Eu sei disso

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

podia funcionar.

brainfck
fonte
19
Observe que o Ruby 2.4 ou superior temarray.sum
dawg
O Ruby 2.6 não possui. Ruby dá, Ruby tira, ao que parece.
Lori
1
@Lori hmm? link
steenslag 26/06/19
Desculpa. Naquele momento, acreditei erroneamente que estava usando o 2.6 por causa de uma falha de rbenv da minha parte.
Lori

Respostas:

612

Tente o seguinte:

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

Veja a documentação numerável do Ruby

(nota: o 0caso base é necessário para que 0seja retornado em uma matriz vazia em vez de nil)

zenazn
fonte
317
jorney's array.inject(:+)é mais eficiente.
Peter
3
array.inject(:+)parece causar problemas no Ruby 1.8.6 Exceções "LocalJumpError: nenhum bloco fornecido" podem aparecer.
Kamil Szot
34
Nos trilhos, array.sumvocê pode somar os valores do array.
Kamil Szot
32
Na maioria dos casos, prefiro usar reduce, que é um apelido de inject(como em array.reduce( :+ )).
Boris Stitnicky
3
@ Boris Além disso, o Rubycop irá avisá-lo por usar injecte não reduce.
Droogans
810

Ou tente o Ruby 1.9:

array.inject(0, :+)

Nota: o 0caso base é necessário, caso contrário nil, será retornado em matrizes vazias:

> [].inject(:+)
nil
> [].inject(0, :+)
0
jomey
fonte
6
Como posso usar dessa maneira para somar um atributo do objeto. Minha matriz [product1, product2] Quero somar product1.price + product2.price. É possível usar array.inject (: +)?
21411 Pablo Cantero
7
Você pode usar um truque semelhante com o método do mapa: array.map (&: preço) .inject (: +)
markquezada
100
array.map(&:price).inject(0, :+)é um pouco mais seguro. Ele garante que, se você tiver uma lista vazia, obtenha 0 em vez de nulo .
johnf
11
usando array.map (...). inject (...) é ineficiente, você repetirá todos os dados duas vezes. Tente array.inject(0) { |sum, product| sum += product.price }
everett1992
4
@ everett1992 e, como se vê, nem mesmo uma otimização. Fazer isso em duas etapas é consistentemente mais rápido para mim. gist.github.com/cameron-martin/b907ec43a9d8b9303bdc
Cameron Martin
290
array.reduce(0, :+)

Embora equivalente array.inject(0, :+), o termo reduzir está entrando em um vernáculo mais comum com o aumento dos modelos de programação do MapReduce .

injetar , reduzir , dobrar , acumular e compactar são sinônimos como uma classe de funções de dobragem . Considero a consistência na sua base de códigos mais importante, mas como várias comunidades preferem uma palavra a outra, é útil conhecer as alternativas.

Para enfatizar o palavreado de redução de mapa, aqui está uma versão que perdoa um pouco o que acaba nessa matriz.

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

Algumas leituras relevantes adicionais:

Evan
fonte
11
Eu concordo, reduceme diz mais sobre o que a função faz, mas injectsoa muito mais legal.
everett1992
1
Concordo com o último comentário, você me deu a melhor resposta.
Jerska
1
O único comentário que eu faria é que, reducee mapcomo as funções de ordem superior são anteriores ao MapReduce. A inspiração segue o contrário. E no sentido do MapReduce, é uma operação um pouco diferente de uma simples redução funcional, com implicações na forma como diferentes máquinas se comunicam.
acjay
Ken Iverson introduziu o operador / chamado "operador de redução" na linguagem de programação APL. Fonte: Iverson, Kenneth. 1962. Uma linguagem de programação. Wiley. Outra fonte: "Notação como uma ferramenta de pensamento" de 1979 ACM Turing Award Lecture, Kenneth E. Iverson, dl.acm.org/ft_gateway.cfm?id=1283935&type=pdf
Fernando Pelliccioni
112

Como alternativa (apenas para comparação), se você tiver o Rails instalado (na verdade, apenas o ActiveSupport):

require 'activesupport'
array.sum
Mike Woodhouse
fonte
12
Versões mais recentes do activesupport não carregam todas as extensões por padrão. Você vai querer ou exigirá apenas o módulo de soma: require 'active_support/core_ext/enumerable.rb'ou exigir de todos o apoio ativo: require 'active_support/all'. Mais sobre isso aqui: API Docs
dcashman 21/03
2
Não importa que activesupporté uma enorme dependência para arrastar em um projeto para ir de array.inject(:+)para array.sum.
meagar
1
Nitpick para um comentário bom: caso contrário, ele deve ser require 'active_support/core_ext/enumerable'sem o .rbsufixo, já que foi adicionado implicitamente.
Por Lundberg
72

Para Ruby> = 2.4.0, você pode usar a sumpartir de Enumerables.

[1, 2, 3, 4].sum

É perigoso mokeypatch classes base. Se você gosta de perigo e usa uma versão mais antiga do Ruby, pode adicionar #sumà Arrayclasse:

class Array
  def sum
    inject(0) { |sum, x| sum + x }
  end
end
jrhicks
fonte
1
Por favor, não faça isso
user3467349
@ user3467349 por quê?
YoTengoUnLCD 27/09/16
15
Monkeypatching classes base não é bom.
user3467349
1
O argumento que ele está argumentando é que você não precisa fazer o Patch do Macaco para Ruby> = 2.4, e que o patch do macaco é perigoso, e que agora você pode somar enumeráveis ​​nativamente, mas também há uma maneira de suportar a funcionalidade.
precisa saber é o seguinte
Voto negativo porque sua implementação retorna nulo em matrizes vazias.
Eldritch Conundrum
45

Novo no Ruby 2.4.0

Você pode usar o método com nome apropriado Enumerable#sum. Tem muitas vantagens, inject(:+)mas existem algumas notas importantes para ler no final.

Exemplos

Gamas

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

Matrizes

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

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

Nota importante

Este método não é equivalente a #inject(:+). Por exemplo

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

Além disso,

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

Veja esta resposta para obter mais informações sobre o motivo sum.

Eli Sadoff
fonte
20

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

Ruby pré 2.4 - array.inject(:+)ouarray.reduce(:+)

* Nota: O #summétodo é uma nova adição à 2.4 para enumerableque agora você poderá usar array.sumo Ruby puro, não apenas o Rails.

coletar
fonte
2
O Ruby 2.4.0 foi lançado hoje com esse recurso incluído! 🎉
amoebe
@amoebe você está correto! Fico feliz em ver esse recurso útil incluído.
colete
19

Apenas por uma questão de diversidade, você também pode fazer isso se sua matriz não for uma matriz de números, mas sim uma matriz de objetos que possuam propriedades que são números (por exemplo, quantidade):

array.inject(0){|sum,x| sum + x.amount}
HashFail
fonte
3
Isto é equivalente a fazer: array.map(&:amount).inject(0, :+). Veja outras respostas.
Richard Jones
4
De certa forma, sim. No entanto, o uso mapentão injectexige que você faça um loop pela matriz duas vezes: uma vez para criar uma nova matriz, a outra para somar os membros. Este método é um pouco mais detalhado, mas também mais eficiente.
HashFail 21/02
Aparentemente, ele não é mais eficiente, consulte gist.github.com/cameron-martin/b907ec43a9d8b9303bdc - crédito para os comentários em esta resposta: stackoverflow.com/a/1538949/1028679
rmcsharry
18

ruby 1.8.7 way é o seguinte:

array.inject(0, &:+) 
Vova
fonte
Se você leu o meu comentário de 2011, e ele ainda é relevante, você está usando o 1.8.6, atualize!
Andrew Grimm
16

Você pode simplesmente usar:

    example = [1,2,3]
    example.inject(:+)
Ganesh Sagare
fonte
Por que isso funciona: inject(:+)mas isso não funciona inject :+?
Arnold Roa
@ArnoldRoa "injetar: +" funciona para mim, que resultado você obteve?
precisa saber é o seguinte
6

Isto é suficiente [1,2,3].inject('+')

Mahesh Bablu
fonte
5

O Ruby 2.4.0 é lançado e possui um método # sum Enumerable . Então você pode fazer

array.sum

Exemplos dos documentos:

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

Também 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
mais grosseiro
fonte
3

para um array com valores nulos, podemos compactar e injetar a soma

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

Também funciona para intervalos ... portanto,

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

Se você sente golfe, pode fazer

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

Isso criará uma sequência "123 + 321 + 12389" e, em seguida, usará a função eval para fazer a soma. Isso é apenas para fins de golfe , você não deve usá-lo no código apropriado.

Ulysse BN
fonte
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: Quando a Matriz contém valores nulos e vazios, por padrão, se você usar alguma das funções acima, reduza, some, injete tudo o que for necessário através do método

TypeError: nil não pode ser coagido em Inteiro

Você pode superar isso,

   [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

Avalia as expressões Ruby na string.

  [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
fonte
1

3 maneiras que podemos fazer soma de matriz

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

2) array.inject('+')

3) array.join('+')

Poonkodi
fonte
0

Ou você pode tentar este método:

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

Este é o caminho mais curto. Tente.

array.inject :+

Tej Poudel
fonte
0
number = [1..100]

number. each do |n|

    final_number = n.sum

    puts "The sum is #{final_number}"
end

* Isso funcionou bem para mim como um novo desenvolvedor. Você pode ajustar seu intervalo de números alterando os valores dentro de []

Madeline Young
fonte
-1

Você também pode fazer isso de maneira fácil

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

Você pode usar .map e .sum como:

array.map { |e| e }.sum
shabdar
fonte
3
Qual é o objetivo de um mapa retornar o mesmo elemento? isso é exatamente o mesmo quearray.sum
Arnold Roa
Além disso, array.sum não existe em ruby. Veja Mike Woodhouse resposta
Ulysse BN 27/11
Faz agora no Ruby 2.4.0
installero