Come sommare la matrice di numeri in Ruby?


564

Ho una matrice di numeri interi.

Per esempio:

array = [123,321,12389]

C'è un modo carino per ottenerne la somma?

Lo so

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

funzionerebbe.


19
Si prega di notare che Ruby 2.4+ haarray.sum
dawg

Ruby 2.6 non ce l'ha. Ruby dà, Ruby toglie, a quanto pare.
Lori,

1
@Lori hmm? link
steenslag,

Scusate. A quel tempo credevo erroneamente che stavo usando 2.6 a causa di un errore rbenv da parte mia.
Lori,

Risposte:


613

Prova questo:

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

Vedi la documentazione enumerabile di Ruby

(nota: il 0caso base è necessario in modo che 0venga restituito su un array vuoto anziché nil)


317
jorney's array.inject(:+)è più efficiente.
Peter,

3
array.inject(:+)sembra causare problemi in Ruby 1.8.6 Potrebbero apparire eccezioni "LocalJumpError: nessun blocco fornito".
Kamil Szot,

34
In rotaie array.sumpotrebbe darti la somma dei valori dell'array.
Kamil Szot,

32
Nella maggior parte dei casi, preferisco usare reduce, che è un alias di inject(come in array.reduce( :+ )).
Boris Stitnicky,

3
@Boris Inoltre, Rubycop ti avviserà per l'utilizzo injectanziché reduce.
Droogans,

810

Oppure prova il modo Ruby 1.9:

array.inject(0, :+)

Nota: il 0caso base è necessario altrimenti nilverrà restituito su array vuoti:

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

6
Come posso usare questo modo per sommare un attributo dall'oggetto. Il mio array [product1, product2] Voglio sommare product1.price + product2.price. È possibile usare array.inject (: +)?
Pablo Cantero,

7
Puoi usare un trucco simile con il metodo map: array.map (&: price) .inject (: +)
markquezada

100
array.map(&:price).inject(0, :+)è un po 'più sicuro. Si assicura che se si dispone di un elenco vuoto si ottiene 0 anziché zero .
johnf

11
l'utilizzo di array.map (...). inject (...) è inefficiente, ripeterai due volte tutti i dati. Prova array.inject(0) { |sum, product| sum += product.price }
everett1992

4
@ everett1992 e, a quanto pare, nemmeno un'ottimizzazione. Farlo in due fasi è sempre più veloce per me. gist.github.com/cameron-martin/b907ec43a9d8b9303bdc
Cameron Martin

290
array.reduce(0, :+)

Sebbene equivalente a array.inject(0, :+), il termine ridurre sta entrando in un vernacolo più comune con l'ascesa dei modelli di programmazione MapReduce .

iniettare , ridurre , piegare , accumulare e comprimere sono tutti sinonimi di una classe di funzioni di piegatura . Trovo che la coerenza tra la tua base di codice sia la più importante, ma poiché varie comunità tendono a preferire una parola rispetto a un'altra, è comunque utile conoscere le alternative.

Per enfatizzare la verbosità della riduzione della mappa, ecco una versione che è un po 'più tollerante su ciò che finisce in quella matrice.

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

Alcune letture pertinenti aggiuntive:


11
Sono d'accordo, reducemi dice di più su ciò che fa la funzione, ma injectsuona molto più fresco.
everett1992,

1
Concordo con l'ultimo commento, mi hai dato la risposta migliore.
Jerska,

1
L'unico commento che vorrei fare è quello reducee mappoiché le funzioni di ordine superiore precedono MapReduce. L'ispirazione va dall'altra parte. E nel senso di MapReduce, è un'operazione leggermente diversa da una semplice riduzione funzionale, con implicazioni per il modo in cui comunicano macchine diverse.
Acjay,

Ken Iverson ha introdotto l'operatore / chiamato "operatore di riduzione" nel linguaggio di programmazione APL. Fonte: Iverson, Kenneth. 1962. Un linguaggio di programmazione. Wiley. Un'altra fonte: "Notazione come strumento di pensiero", conferenza ACM Turing Award 1979, Kenneth E. Iverson, dl.acm.org/ft_gateway.cfm?id=1283935&type=pdf
Fernando Pelliccioni,

112

In alternativa (solo per confronto), se hai installato Rails (in realtà solo ActiveSupport):

require 'activesupport'
array.sum

12
Le versioni più recenti di activesupport in realtà non caricano tutte le estensioni per impostazione predefinita. Ti consigliamo di entrambi richiedono solo il modulo sum: require 'active_support/core_ext/enumerable.rb'o richiedono a tutte le sostegno attivo: require 'active_support/all'. Maggiori informazioni qui: API Docs
dcashman

2
Non importa che activesupportè una massiccia dipendenza trascinare in un progetto per andare da array.inject(:+)a array.sum.
meagar

1
Nitpick a un commento altrimenti buono: dovrebbe essere require 'active_support/core_ext/enumerable'senza il .rbsuffisso, poiché è stato aggiunto implicitamente.
Per Lundberg,

72

Per Ruby> = 2.4.0 è possibile utilizzare sumda Enumerables.

[1, 2, 3, 4].sum

È pericoloso classificare le classi base. Se ti piace il pericolo e usi una versione precedente di Ruby, potresti aggiungere #sumalla Arrayclasse:

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

1
Per favore, non farlo
user3467349

@ user3467349 perché?
YoTengoUnLCD,

15
Le classi base di monkeypatch non sono belle.
user3467349

1
Il punto che sta sottolineando è che non è necessario eseguire Monkey Patch per Ruby> = 2.4 e che il patching delle scimmie è pericoloso e che ora è possibile sommare gli enumerabili in modo nativo, ma esiste anche un modo per eseguire il backport della funzionalità.
Peter H. Boling,

Sottovalutato perché l'implementazione restituisce zero su array vuoti.
Eldritch Conundrum,

45

Novità per Ruby 2.4.0

È possibile utilizzare il metodo con nome appropriato Enumerable#sum. Ha molti vantaggi, inject(:+)ma alla fine ci sono anche alcune note importanti da leggere.

Esempi

Intervalli

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

Array

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

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

Nota importante

Questo metodo non è equivalente a #inject(:+). Per esempio

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

Anche,

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

Vedi questa risposta per maggiori informazioni sul perché sumè così.


20

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

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

* Nota: il #summetodo è una nuova aggiunta alla versione 2.4 per enumerablecui ora sarai in grado di utilizzare array.sumil rubino puro, non solo Rails.


2
Ruby 2.4.0 è stato rilasciato oggi con questa funzione inclusa! 🎉
amebe, il

@amoebe hai ragione! Sono contento di vedere questa utile funzione inclusa.
raccogli il

19

Solo per motivi di diversità, puoi anche farlo se la tua matrice non è una matrice di numeri, ma piuttosto una matrice di oggetti che hanno proprietà che sono numeri (ad esempio quantità):

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

3
Ciò equivale a fare: array.map(&:amount).inject(0, :+). Vedi altre risposte
Richard Jones,

4
In un certo senso, sì. Tuttavia, l'utilizzo richiede mapquindi injectdi passare in rassegna l'array due volte: una volta per creare un nuovo array, l'altro per sommare i membri. Questo metodo è leggermente più dettagliato, ma anche più efficiente.
HashFail,

A quanto pare non è più efficiente, vedere gist.github.com/cameron-martin/b907ec43a9d8b9303bdc - credito per i commenti in questa risposta: stackoverflow.com/a/1538949/1028679
rmcsharry

18

ruby 1.8.7 way è il seguente:

array.inject(0, &:+) 

Se leggi il mio commento del 2011, ed è ancora pertinente mentre stai usando 1.8.6, esegui l'upgrade!
Andrew Grimm,

16

Puoi semplicemente usare:

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

Perché funziona: inject(:+)ma non funziona inject :+?
Arnold Roa,

@ArnoldRoa "inject: +" funziona per me, che risultato hai ottenuto?
Ganesh Sagare,


5

Ruby 2.4.0 viene rilasciato e ha un metodo Enumerable # sum . Quindi puoi farlo

array.sum

Esempi dai documenti:

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

3

Inoltre consente di [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

per array con valori nulli possiamo fare compatto e quindi iniettare la somma ex-

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

2
array.reduce(:+)

Funziona anche per Ranges ... quindi,

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

1

Se ti senti golfy, puoi farlo

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

Ciò creerà una stringa "123 + 321 + 12389" e quindi utilizzerà la funzione eval per fare la somma. Questo è solo a scopo di golf , non dovresti usarlo nel giusto codice.


1

Metodo 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

Metodo 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 `+'

Metodo 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 `+'

Metodo 4: Quando l'array contiene valori nulli e vuoti, per impostazione predefinita se si utilizza una delle funzioni sopra, ridurre, sommare, iniettare tutto ciò che

TypeError: zero non può essere forzato in Integer

Puoi superare questo,

   [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

Metodo 6: eval

Valuta le espressioni Ruby nella stringa.

  [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 modi in cui possiamo fare la somma dell'array

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

2) array.inject('+')

3) array.join('+')


0

Oppure puoi provare questo metodo:

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

* Questo ha funzionato bene per me come nuovo sviluppatore. È possibile regolare l'intervallo di numeri modificando i valori all'interno di []


-1

Puoi anche farlo in modo semplice

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.