224 votes

Comment créer une moyenne à partir d'un tableau Ruby ?

Comment trouver une moyenne à partir d'un tableau ?

Si j'ai le tableau :

[0,4,8,2,5,0,2,6]

La moyenne me donnerait 3,375.

12 votes

Si vous obtenez 21,75 comme moyenne de ces chiffres, quelque chose ne va pas du tout...

2 votes

Dotty, je ne sais pas comment vous avez obtenu 21,75, mais la moyenne pour cet ensemble de données est de 3,375 et la somme est de 27. Je ne sais pas quelle sorte de fonction d'agrégation pourrait donner 21,75. Vérifiez que la moyenne est bien celle que vous recherchez !

2 votes

Je n'ai AUCUNE idée d'où je suis arrivé à 21.75. J'ai dû appuyer sur quelque chose comme 0+48+2+5+0+2+6 sur la calculatrice !

268voto

John Feminella Points 116878

Essayez ça :

arr = [5, 6, 7, 8]
arr.inject{ |sum, el| sum + el }.to_f / arr.size
=> 6.5

Notez le .to_f qui vous permettra d'éviter tout problème lié à la division d'un nombre entier. Vous pouvez aussi le faire :

arr = [5, 6, 7, 8]
arr.inject(0.0) { |sum, el| sum + el } / arr.size
=> 6.5

Vous pouvez le définir comme faisant partie de Array comme l'a suggéré un autre commentateur, mais vous devez éviter la division d'entiers ou vos résultats seront faux. De plus, ceci n'est généralement pas applicable à tous les types d'éléments possibles (évidemment, une moyenne n'a de sens que pour les choses qui peuvent être moyennées). Mais si vous voulez suivre cette voie, utilisez ceci :

class Array
  def sum
    inject(0.0) { |result, el| result + el }
  end

  def mean 
    sum / size
  end
end

Si vous n'avez pas vu inject avant, ce n'est pas aussi magique qu'il n'y paraît. Il itère sur chaque élément et lui applique ensuite une valeur d'accumulation. L'accumulateur est ensuite transmis à l'élément suivant. Dans ce cas, notre accumulateur est simplement un nombre entier qui reflète la somme de tous les éléments précédents.

Edit : Le commentateur Dave Ray a proposé une belle amélioration.

Edit : La proposition du commentateur Glenn Jackman, qui utilise arr.inject(:+).to_f est également agréable, mais peut-être un peu trop intelligente si vous ne savez pas ce qui se passe. Le site :+ est un symbole ; lorsqu'il est passé à inject, il applique la méthode nommée par le symbole (dans ce cas, l'opération d'addition) à chaque élément contre la valeur de l'accumulateur.

6 votes

Vous pouvez éliminer l'opérateur to_f et ? en passant une valeur initiale à injecter : arr.inject(0.0) { |sum,el| sum + el } / arr.size .

106 votes

Ou : arr.inject(:+).to_f / arr.size # => 3.375

0 votes

@glenn : C'est bien aussi, mais je pense que la conversion automatique Symbol#to_proc est un peu trop intelligente pour une réponse StackOverflow.

120voto

Corban Brook Points 8836
a = [0,4,8,2,5,0,2,6]
a.instance_eval { reduce(:+) / size.to_f } #=> 3.375

Une version de ce système qui n'utilise pas instance_eval serait :

a = [0,4,8,2,5,0,2,6]
a.reduce(:+) / a.size.to_f #=> 3.375

0 votes

Salut, Corban. Je suis à la recherche de documentation sur ":+". Cela fait-il partie d'une "classe" de symboles qu'une fonction, comme reduce, peut utiliser ? Merci.

0 votes

Celui-ci pourrait être considéré comme trop intelligent, mais très utile lorsque vous êtes à l'intérieur de la session console et que vous ne voulez pas étendre la classe Array ou stocker Array dans une variable séparée, donc +1 définitivement !

6 votes

Je ne pense pas que ce soit trop intelligent. Je pense qu'elle résout le problème de manière idiomatique. C'est-à-dire qu'il utilise reduce, ce qui est exactement correct. Les programmeurs devraient être encouragés à comprendre ce qui est correct, pourquoi c'est correct, puis à le propager. Pour une opération triviale comme moyenne, vrai, on n'a pas besoin d'être "intelligent". Mais en comprenant ce qu'est "reduce" pour un cas trivial, on peut ensuite commencer à l'appliquer à des problèmes beaucoup plus complexes. upvote.

96voto

Shu Wu Points 616

Je crois que la réponse la plus simple est

list.reduce(:+).to_f / list.size

1 votes

Il m'a fallu un moment pour le trouver reduce est une méthode de la Enumerable utilisé par Array . Et malgré son nom, je suis d'accord avec le @ShuWu ... à moins que vous n'utilisiez Rails qui implémente sum .

0 votes

Je vois ici des solutions que je connais et qui ont l'air extrêmement soignées, mais j'ai peur que si je lis mon code dans le futur, elles ressemblent à du charabia. Merci pour cette solution propre !

0 votes

Sur mon système, c'est 3x plus rapide que la réponse acceptée.

55voto

Denny Abraham Points 362

J'espérais obtenir Math.average(values), mais pas de chance.

values = [0,4,8,2,5,0,2,6]
average = values.sum / values.size.to_f

3 votes

Je n'avais pas réalisé que #sum avait été ajouté par Rails ! Merci de l'avoir souligné.

15 votes

Après Noël 2016 (Ruby 2.4), Array sera ont un sum méthode, donc cela semble être une réponse correcte après 6 ans, digne du prix Nostradamus.

15voto

Dmitry Polushkin Points 920

Pourquoi ne pas simplement :

list.sum.to_f / list.size

C'est plus lisible et plus facile à écrire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X