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.


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)


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

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:


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

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

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.


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.


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}

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

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

Warum funktioniert das: inject(:+)aber das nicht inject :+?
Arnold Roa

@ArnoldRoa "injizieren: +" es funktioniert für mich, welches Ergebnis hast du bekommen?
Ganesh Sagare


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

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

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

2
array.reduce(:+)

Funktioniert auch für Bereiche ... daher

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

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.


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+

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


0

Oder Sie können diese Methode ausprobieren:

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

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


-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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.