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.


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)


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

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:


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

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

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.


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.


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}

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, &:+) 

Se você leu o meu comentário de 2011, e ele ainda é relevante, você está usando o 1.8.6, atualize!
Andrew Grimm



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

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

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(:+)

2
array.reduce(:+)

Também funciona para intervalos ... portanto,

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

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.


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+

1

3 maneiras que podemos fazer soma de matriz

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

2) array.inject('+')

3) array.join('+')


0

Ou você pode tentar 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

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


-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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.