Wie summiere ich eine Reihe von Zahlen in Ruby?

563

Ich habe eine Reihe von ganzen Zahlen.

Zum Beispiel:

array = [123,321,12389]

Gibt es eine gute Möglichkeit, die Summe zu ermitteln?

Ich weiß das

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

würde funktionieren.

Brainfck
quelle
19
Bitte beachten Sie, dass Ruby 2.4+ hatarray.sum
dawg
Ruby 2.6 hat es nicht. Ruby gibt, Ruby nimmt weg, wie es scheint.
Lori
1
@ Lori hmm? Link
Steenslag
Es tut uns leid. Zu dieser Zeit glaubte ich fälschlicherweise, ich würde 2.6 verwenden, weil meinerseits ein Rbenv-Ausrutscher auftrat.
Lori

Antworten:

612

Versuche dies:

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

Siehe Rubys Aufzählungsdokumentation

(Hinweis: Der 0Basisfall wird benötigt, damit er 0auf einem leeren Array zurückgegeben wird. nil)

Zenazn
quelle
317
jorney's array.inject(:+)ist effizienter.
Peter
3
array.inject(:+)scheint Probleme in Ruby 1.8.6 zu verursachen. Ausnahmen "LocalJumpError: kein Block angegeben" könnten auftauchen.
Kamil Szot
34
In Rails erhalten Sie array.summöglicherweise die Summe der Array-Werte.
Kamil Szot
32
In den meisten Fällen bevorzuge ich die Verwendung reduceeines Alias ​​von inject(wie in array.reduce( :+ )).
Boris Stitnicky
3
@Boris Rubycop warnt Sie auch vor der Verwendung von injectanstatt reduce.
Droogans
810

Oder probieren Sie Ruby 1.9:

array.inject(0, :+)

Hinweis: Der 0Basisfall wird benötigt, andernfalls nilwird er auf leeren Arrays zurückgegeben:

> [].inject(:+)
nil
> [].inject(0, :+)
0
Jomey
quelle
6
Wie kann ich auf diese Weise ein Attribut aus einem Objekt summieren? Mein Array [Produkt1, Produkt2] Ich möchte Produkt1.Preis + Produkt2.Preis summieren. Ist es möglich, array.inject (: +) zu verwenden?
Pablo Cantero
7
Sie können einen ähnlichen Trick mit der Kartenmethode verwenden: array.map (&: price) .inject (: +)
markquezada
100
array.map(&:price).inject(0, :+)ist ein bisschen sicherer. Es stellt sicher, dass Sie bei einer leeren Liste 0 anstelle von Null erhalten .
Johnf
11
Wenn Sie array.map (...) verwenden, ist injizieren (...) ineffizient. Sie werden alle Daten zweimal durchlaufen. Versuchen Sie array.inject(0) { |sum, product| sum += product.price }
everett1992
4
@ everett1992 und wie sich herausstellt, überhaupt keine Optimierung. Das in zwei Schritten zu machen ist für mich immer schneller. gist.github.com/cameron-martin/b907ec43a9d8b9303bdc
Cameron Martin
290
array.reduce(0, :+)

Während entspricht array.inject(0, :+), der Begriff reduce eine üblichere Umgangssprache mit dem Anstieg des eintritt MapReduce Programmiermodelle .

Injizieren , Reduzieren , Falten , Akkumulieren und Komprimieren sind synonym eine Klasse von Faltfunktionen . Ich finde Konsistenz in Ihrer Codebasis am wichtigsten, aber da verschiedene Communities dazu neigen, ein Wort einem anderen vorzuziehen, ist es dennoch nützlich, die Alternativen zu kennen.

Um die kartenreduzierte Sprache hervorzuheben, ist hier eine Version, die etwas verzeihender ist, was in diesem Array endet.

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

Einige zusätzliche relevante Lektüre:

Evan
quelle
11
Ich stimme zu, reduceerzähle mir mehr darüber, was die Funktion macht, injectklingt aber viel cooler.
everett1992
1
Stimmen Sie dem letzten Kommentar zu, Sie haben mir die beste Antwort gegeben.
Jerska
1
Der einzige Kommentar , den ich machen würde , ist , dass reduceund mapwie Funktionen höherer Ordnung MapReduce predate. Die Inspiration läuft in die andere Richtung. Im Sinne von MapReduce handelt es sich um eine etwas andere Operation als eine einfache Funktionsreduzierung, die Auswirkungen auf die Kommunikation verschiedener Maschinen hat.
Acjay
Ken Iverson führte den Operator / "Reduktionsoperator" in der Programmiersprache APL ein. Quelle: Iverson, Kenneth. 1962. Eine Programmiersprache. Wiley. Eine andere Quelle: "Notation als Werkzeug des Denkens", 1979 ACM Turing Award Lecture, Kenneth E. Iverson, dl.acm.org/ft_gateway.cfm?id=1283935&type=pdf
Fernando Pelliccioni
112

Alternativ (nur zum Vergleich), wenn Sie Rails installiert haben (eigentlich nur ActiveSupport):

require 'activesupport'
array.sum
Mike Woodhouse
quelle
12
Neuere Versionen von activesupport laden standardmäßig nicht alle Erweiterungen. Sie möchten entweder nur das Summenmodul benötigen: require 'active_support/core_ext/enumerable.rb'oder die gesamte aktive Unterstützung : require 'active_support/all'. Mehr dazu hier: API Docs
dcashman
2
Es ist egal, dass dies activesupporteine massive Abhängigkeit ist, um in ein Projekt zu ziehen, von array.inject(:+)dem aus zu gehen array.sum.
Meagar
1
Nitpick zu einem ansonsten guten Kommentar: Es sollte require 'active_support/core_ext/enumerable'ohne das .rbSuffix sein, da dies implizit hinzugefügt wird.
Per Lundberg
72

Für Ruby> = 2.4.0 können Sie sumaus Enumerables verwenden.

[1, 2, 3, 4].sum

Es ist gefährlich, Basisklassen zu mokeypatchen. Wenn Sie Gefahr mögen und eine ältere Version von Ruby verwenden, können Sie #sumder ArrayKlasse Folgendes hinzufügen :

class Array
  def sum
    inject(0) { |sum, x| sum + x }
  end
end
jrhicks
quelle
1
Bitte nicht tun
user3467349
@ user3467349 warum?
YoTengoUnLCD
15
Monkeypatching Basisklassen ist nicht schön.
user3467349
1
Der Punkt, den er macht, ist, dass Sie den Monkey Patch für Ruby> = 2.4 nicht ausführen müssen und dass das Patchen von Affen gefährlich ist und dass Sie jetzt Enumerables nativ summieren können, aber es gibt auch eine Möglichkeit, die Funktionalität zurück zu portieren.
Peter H. Boling
Downvoted, da Ihre Implementierung bei leeren Arrays null zurückgibt.
Eldritch Conundrum
45

Neu für Ruby 2.4.0

Sie können die treffend benannte Methode verwenden Enumerable#sum. Es hat viele Vorteile gegenüber, inject(:+)aber am Ende gibt es auch einige wichtige Hinweise zu lesen.

Beispiele

Bereiche

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

Arrays

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

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

Wichtige Notiz

Diese Methode ist nicht gleichbedeutend mit #inject(:+). Zum Beispiel

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

Ebenfalls,

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

In dieser Antwort finden Sie weitere Informationen dazu, warum sumdies so ist.

Eli Sadoff
quelle
20

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

Ruby vor 2.4 - array.inject(:+)oderarray.reduce(:+)

* Hinweis: Die #sumMethode ist eine neue Ergänzung zu 2.4, enumerablesodass Sie sie jetzt array.sumin reinem Rubin verwenden können, nicht nur in Rails.

sammeln
quelle
2
Ruby 2.4.0 wurde heute mit dieser Funktion veröffentlicht! 🎉
Amöbe
@amoebe du bist richtig! Ich bin froh, dass diese nützliche Funktion enthalten ist.
Sammeln Sie
19

Nur aus Gründen der Vielfalt können Sie dies auch tun, wenn Ihr Array kein Array von Zahlen ist, sondern ein Array von Objekten mit Eigenschaften, die Zahlen sind (z. B. Betrag):

array.inject(0){|sum,x| sum + x.amount}
HashFail
quelle
3
Dies entspricht : array.map(&:amount).inject(0, :+). Siehe andere Antworten.
Richard Jones
4
In gewisser Weise ja. Allerdings verwenden mapdann injectmüssen Sie zweimal durch die Array - Schleife: einmal ein neues Array zu erstellen, die andere die Mitglieder zu summieren. Diese Methode ist etwas ausführlicher, aber auch effizienter.
HashFail
Anscheinend ist es nicht effizienter, siehe gist.github.com/cameron-martin/b907ec43a9d8b9303bdc - Gutschrift auf die Kommentare in dieser Antwort: stackoverflow.com/a/1538949/1028679
rmcsharry
18

Ruby 1.8.7 Weg ist der folgende:

array.inject(0, &:+) 
Vova
quelle
Wenn Sie meinen Kommentar von 2011 gelesen haben und er bei Verwendung von 1.8.6 immer noch relevant ist, aktualisieren Sie ihn bitte!
Andrew Grimm
16

Sie können einfach verwenden:

    example = [1,2,3]
    example.inject(:+)
Ganesh Sagare
quelle
Warum funktioniert das: inject(:+)aber das nicht inject :+?
Arnold Roa
@ArnoldRoa "injizieren: +" es funktioniert für mich, welches Ergebnis hast du bekommen?
Ganesh Sagare
6

Das ist genug [1,2,3].inject('+')

Mahesh Bablu
quelle
5

Ruby 2.4.0 ist veröffentlicht und verfügt über eine Enumerable # sum- Methode. So können Sie tun

array.sum

Beispiele aus den Dokumenten:

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

Ermöglicht auch [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
gröber
quelle
3

Für Arrays mit Nullwerten können wir kompakt machen und dann die Summe ex- injizieren

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

Funktioniert auch für Bereiche ... daher

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

Wenn Sie sich golfig fühlen, können Sie tun

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

Dadurch wird eine Zeichenfolge "123 + 321 + 12389" erstellt und anschließend die Funktion eval mit der Funktion eval erstellt. Dies ist nur zum Golfen gedacht , Sie sollten es nicht im richtigen Code verwenden.

Ulysse BN
quelle
1

Methode 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

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

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

Methode 4: Wenn das Array null und leere Werte enthält, reduzieren, summieren und injizieren Sie standardmäßig alles, wenn Sie eine der oben genannten Funktionen verwenden

TypeError: nil kann nicht in Integer gezwungen werden

Sie können dies überwinden, indem Sie

   [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

Methode 6: Bewertung

Wertet die Ruby-Ausdrücke in der Zeichenfolge aus.

  [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.
quelle
1

3 Möglichkeiten, wie wir die Summe der Arrays machen können

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

2) array.inject('+')

3) array.join('+')

Poonkodi
quelle
0

Oder Sie können diese Methode ausprobieren:

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

Dies ist der kürzeste Weg. Versuch es.

array.inject :+

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

number. each do |n|

    final_number = n.sum

    puts "The sum is #{final_number}"
end

* Das hat für mich als neuen Entwickler gut funktioniert. Sie können Ihren Nummernkreis anpassen, indem Sie die Werte in [] ändern.

Madeline Young
quelle
-1

Sie können es auch auf einfache Weise tun

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

Sie können .map und .sum wie folgt verwenden :

array.map { |e| e }.sum
Shabdar
quelle
3
Was bringt es, wenn eine Karte dasselbe Element zurückgibt? Dies ist genau das gleiche wiearray.sum
Arnold Roa
Außerdem existiert array.sum nicht in Ruby. Siehe Mike Woodhouse Antwort
Ulysse BN
Es tut jetzt in Ruby 2.4.0
installero