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.