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.


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)


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

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:


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

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

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.


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.


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}

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

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

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


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

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

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

2
array.reduce(:+)

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

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

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.


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+

1

3 façons de faire la somme des tableaux

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

2) array.inject('+')

3) array.join('+')


0

Ou vous pouvez essayer cette méthode:

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

* 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 []


-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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.