Comment additionner un tableau de nombres en Ruby?

564

J'ai un tableau d'entiers.

Par exemple:

array = [123,321,12389]

Y a-t-il un bon moyen d'en obtenir la somme?

Je le sais

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

travaillerait.

brainfck
la source
19
Veuillez noter que Ruby 2.4+ aarray.sum
dawg
Ruby 2.6 ne l'a pas. Ruby donne, Ruby enlève, semble-t-il.
Lori
1
@Lori hmm? link
steenslag
Désolé. À ce moment-là, j'ai cru à tort que j'utilisais 2.6 à cause d'une erreur de rbenv de ma part.
Lori

Réponses:

613

Essaye ça:

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

Voir la documentation énumérable de Ruby

(note: le 0cas de base est nécessaire pour qu'il 0soit retourné sur un tableau vide au lieu de nil)

Zenazn
la source
317
jorney's array.inject(:+)est plus efficace.
Peter
3
array.inject(:+)semble causer des problèmes dans Ruby 1.8.6 Exceptions "LocalJumpError: aucun bloc donné" pourrait apparaître.
Kamil Szot
34
Dans rails array.sumpeut vous donner la somme des valeurs du tableau.
Kamil Szot
32
Dans la plupart des cas, je préfère utiliser reduce, qui est un alias de inject(comme dans array.reduce( :+ )).
Boris Stitnicky
3
@Boris Aussi, Rubycop vous avertira d'utiliser injectplutôt que reduce.
Droogans
810

Ou essayez la méthode Ruby 1.9:

array.inject(0, :+)

Remarque: le 0cas de base est nécessaire sinon nilil sera retourné sur des tableaux vides:

> [].inject(:+)
nil
> [].inject(0, :+)
0
jomey
la source
6
Comment puis-je utiliser cette méthode pour additionner un attribut à partir d'un objet. Mon tableau [product1, product2] Je veux additionner product1.price + product2.price. Est-il possible d'utiliser array.inject (: +)?
Pablo Cantero
7
Vous pouvez utiliser une astuce similaire avec la méthode map: array.map (&: price) .inject (: +)
markquezada
100
array.map(&:price).inject(0, :+)est un peu plus sûr. Il s'assure que si vous avez une liste vide, vous obtenez 0 au lieu de zéro .
johnf
11
en utilisant array.map (...). inject (...) est inefficace, vous parcourrez toutes les données deux fois. Essayez array.inject(0) { |sum, product| sum += product.price }
everett1992
4
@ everett1992 et en fait, pas même une optimisation du tout. Le faire en deux étapes est toujours plus rapide pour moi. gist.github.com/cameron-martin/b907ec43a9d8b9303bdc
Cameron Martin
290
array.reduce(0, :+)

Bien qu'équivalent à array.inject(0, :+), le terme réduire entre dans une langue vernaculaire plus courante avec l'essor des modèles de programmation MapReduce .

injecter , réduire , plier , accumuler et compresser sont tous synonymes d'une classe de fonctions de pliage . Je trouve la cohérence dans votre base de code la plus importante, mais comme diverses communautés ont tendance à préférer un mot à un autre, il est néanmoins utile de connaître les alternatives.

Pour souligner le verbiage de réduction de carte, voici une version qui est un peu plus indulgente sur ce qui se retrouve dans ce tableau.

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

Quelques lectures pertinentes supplémentaires:

Evan
la source
11
Je suis d'accord, reduceme dit plus sur ce que fait la fonction, mais injectsonne beaucoup plus cool.
everett1992
1
D'accord avec le dernier commentaire, tu m'as donné la meilleure réponse.
Jerska
1
Le seul commentaire que je ferais est que reduceet mapcomme les fonctions d'ordre supérieur sont antérieures à MapReduce. L'inspiration va dans l'autre sens. Et dans le sens MapReduce, il s'agit d'une opération quelque peu différente d'une simple réduction fonctionnelle, ayant des implications sur la façon dont les différentes machines communiquent.
acjay
Ken Iverson a introduit l'opérateur / appelé "opérateur de réduction" dans le langage de programmation APL. Source: Iverson, Kenneth. 1962. Un langage de programmation. Wiley. Une autre source: "Notation as a Tool of Thought", 1979 ACM Turing Award Lecture, Kenneth E. Iverson, dl.acm.org/ft_gateway.cfm?id=1283935&type=pdf
Fernando Pelliccioni
112

Alternativement (juste pour comparaison), si vous avez installé Rails (en fait juste ActiveSupport):

require 'activesupport'
array.sum
Mike Woodhouse
la source
12
Les nouvelles versions d'Activesupport ne chargent pas réellement toutes les extensions par défaut. Vous voudrez soit juste besoin du module de somme: require 'active_support/core_ext/enumerable.rb', ou besoin tout soutien actif: require 'active_support/all'. Pour en savoir plus, cliquez ici: API Docs
dcashman
2
Peu importe, c'est activesupportune dépendance massive à glisser dans un projet pour aller de array.inject(:+)à array.sum.
meagar
1
Nitpick à un autre bon commentaire: il devrait être require 'active_support/core_ext/enumerable'sans le .rbsuffixe, car il est ajouté implicitement.
Per Lundberg
72

Pour Ruby> = 2.4.0, vous pouvez utiliser sumdepuis Enumerables.

[1, 2, 3, 4].sum

Il est dangereux de mokeypatch les classes de base. Si vous aimez le danger et utilisez une ancienne version de Ruby, vous pouvez ajouter #sumà la Arrayclasse:

class Array
  def sum
    inject(0) { |sum, x| sum + x }
  end
end
jrhicks
la source
1
Veuillez ne pas le faire
user3467349
@ user3467349 pourquoi?
YoTengoUnLCD
15
Monkeypatching les classes de base n'est pas sympa.
user3467349
1
Le point qu'il fait valoir est que vous n'avez pas besoin de faire le patch de singe pour Ruby> = 2.4, et que le patch de singe est dangereux, et que vous pouvez maintenant additionner les énumérables en natif, mais il existe également un moyen de rétroporter la fonctionnalité.
Peter H. Boling du
Voté car votre implémentation retourne nil sur des tableaux vides.
Eldritch Conundrum
45

Nouveau pour Ruby 2.4.0

Vous pouvez utiliser la méthode bien nommée Enumerable#sum. Il présente de nombreux avantages, inject(:+)mais il y a également des notes importantes à lire à la fin.

Exemples

Gammes

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

Tableaux

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

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

Note importante

Cette méthode n'est pas équivalente à #inject(:+). Par exemple

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

Aussi,

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

Voir cette réponse pour plus d'informations sur pourquoi sumest-ce comme ça.

Eli Sadoff
la source
20

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

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

* Remarque: La #summéthode est un nouvel ajout à la version 2.4 pour enumerableque vous puissiez désormais l'utiliser array.sumen rubis pur, pas seulement Rails.

collecte
la source
2
Ruby 2.4.0 est sorti aujourd'hui avec cette fonctionnalité incluse! 🎉
amoebe
@amoebe vous avez raison! Heureux de voir cette fonctionnalité utile incluse.
récupérer le
19

Dans un souci de diversité, vous pouvez également le faire si votre tableau n'est pas un tableau de nombres, mais plutôt un tableau d'objets qui ont des propriétés qui sont des nombres (par exemple quantité):

array.inject(0){|sum,x| sum + x.amount}
HashFail
la source
3
Cela équivaut à faire: array.map(&:amount).inject(0, :+). Voir d'autres réponses.
Richard Jones
4
D'une certaine manière, oui. Cependant, en utilisant mapalors injectvous devez faire une boucle à travers le réseau deux fois: une fois pour créer un nouveau tableau, l'autre pour sommer les membres. Cette méthode est légèrement plus verbeuse, mais aussi plus efficace.
HashFail
Apparemment, ce n'est pas plus efficace, voir gist.github.com/cameron-martin/b907ec43a9d8b9303bdc - crédit aux commentaires dans cette réponse: stackoverflow.com/a/1538949/1028679
rmcsharry
18

ruby 1.8.7 way est le suivant:

array.inject(0, &:+) 
Vova
la source
Si vous lisez mon commentaire de 2011 et qu'il est toujours pertinent lorsque vous utilisez la version 1.8.6, veuillez mettre à niveau!
Andrew Grimm
16

Vous pouvez simplement utiliser:

    example = [1,2,3]
    example.inject(:+)
Ganesh Sagare
la source
Pourquoi cela fonctionne: inject(:+)mais cela ne fonctionne pas inject :+?
Arnold Roa
@ArnoldRoa "inject: +" ses travaux pour moi, quel résultat avez-vous obtenu?
Ganesh Sagare
6

C'est assez [1,2,3].inject('+')

Mahesh Bablu
la source
5

Ruby 2.4.0 est sorti, et il a une méthode de somme Enumerable # . Vous pouvez donc faire

array.sum

Exemples tirés de la documentation:

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

Permet également [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
plus grossier
la source
3

pour un tableau avec des valeurs nulles, nous pouvons faire compact, puis injecter la somme ex-

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

Fonctionne également pour les plages ... par conséquent,

(1..10).reduce(:+) returns 55
MulleOne
la source
1

Si vous vous sentez golfé, vous pouvez le faire

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

Cela va créer une chaîne "123 + 321 + 12389" puis utiliser la fonction eval pour faire la somme. C'est uniquement à des fins de golf , vous ne devez pas l'utiliser dans le bon code.

Ulysse BN
la source
1

Méthode 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éthode 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éthode 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éthode 4: lorsque Array contient un nil et des valeurs vides, par défaut si vous utilisez l'une des fonctions ci-dessus, réduisez, additionnez, injectez tout sera par le

TypeError: aucun ne peut être contraint en entier

Vous pouvez surmonter cela en,

   [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éthode 6: eval

Évalue les expressions Ruby dans une chaîne.

  [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
la source
1

3 façons de faire la somme des tableaux

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

2) array.inject('+')

3) array.join('+')

Poonkodi
la source
0

Ou vous pouvez essayer cette méthode:

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

C'est le chemin le plus court. Essayez-le.

array.inject :+

Tej Poudel
la source
0
number = [1..100]

number. each do |n|

    final_number = n.sum

    puts "The sum is #{final_number}"
end

* Cela a bien fonctionné pour moi en tant que nouveau développeur. Vous pouvez ajuster votre plage de numéros en modifiant les valeurs dans []

Madeline Young
la source
-1

Vous pouvez également le faire de manière simple

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

Vous pouvez utiliser .map et .sum comme:

array.map { |e| e }.sum
shabdar
la source
3
Quel est l'intérêt de faire une carte renvoyant le même élément? c'est exactement la même chose quearray.sum
Arnold Roa
De plus array.sum n'existe pas en ruby. Voir la réponse de Mike Woodhouse
Ulysse BN
Il fait maintenant dans Ruby 2.4.0
installero