Jak zsumować tablicę liczb w Ruby?


563

Mam tablicę liczb całkowitych.

Na przykład:

array = [123,321,12389]

Czy jest jakiś dobry sposób, aby uzyskać ich sumę?

Wiem to

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

pracowałbym.


19
Należy pamiętać, że Ruby 2.4+ maarray.sum
dawg

Ruby 2.6 go nie ma. Ruby daje, wydaje się, że Ruby zabiera.
Lori

1
@Lori hmm? link
steenslag

Przepraszam. W tym czasie błędnie uwierzyłem, że używam 2.6 z powodu wpadki z mojej strony.
Lori

Odpowiedzi:


612

Spróbuj tego:

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

Zobacz wyliczalną dokumentację Ruby

(uwaga: 0potrzebna jest skrzynka podstawowa, aby 0zamiast niej została zwrócona pusta tablica nil)


317
jorney's array.inject(:+)jest bardziej wydajny.
Peter,

3
array.inject(:+)wydaje się powodować problemy w wyjątkach Ruby 1.8.6 „LocalJumpError: nie podano żadnego bloku” może się pojawić.
Kamil Szot

34
W szynach array.summoże dać ci sumę wartości tablicy.
Kamil Szot

32
W większości przypadków wolę używać reduce, który jest aliasem inject(jak w array.reduce( :+ )).
Boris Stitnicky

3
@Boris Również Rubycop ostrzeże cię za korzystanie injectzamiast reduce.
Droogans,

810

Lub wypróbuj sposób Ruby 1.9:

array.inject(0, :+)

Uwaga: 0skrzynka podstawowa jest potrzebna, w przeciwnym razie nilzostaną zwrócone w pustych tablicach:

> [].inject(:+)
nil
> [].inject(0, :+)
0

6
Jak mogę użyć tego sposobu do zsumowania atrybutu z obiektu. Moja tablica [produkt1, produkt2] Chcę zsumować produkt1. cena + produkt2. cena. Czy jest to możliwe przy użyciu array.inject (: +)?
Pablo Cantero

7
Możesz użyć podobnej sztuczki w metodzie mapy: array.map (&: price) .inject (: +)
markquezada

100
array.map(&:price).inject(0, :+)jest nieco bezpieczniejszy. Zapewnia to, że jeśli masz pustą listę, dostajesz 0 zamiast zera .
johnf

11
za pomocą array.map (...). inject (...) jest nieefektywny, powtórzysz wszystkie dane dwa razy. Spróbuj array.inject(0) { |sum, product| sum += product.price }
everett1992

4
@ everett1992 i jak się okazuje, nie ma nawet żadnej optymalizacji. Robienie tego w dwóch etapach jest dla mnie konsekwentnie szybsze. gist.github.com/cameron-martin/b907ec43a9d8b9303bdc
Cameron Martin

290
array.reduce(0, :+)

Choć odpowiednik array.inject(0, :+)terminu „ redukuj” wchodzi w bardziej popularne języki wraz ze wzrostem modeli programowania MapReduce .

wstrzykiwanie , zmniejszanie , składanie , gromadzenie i kompresja są synonimami jako klasa funkcji składania . Najważniejsza jest spójność w całej bazie kodu, ale ponieważ różne społeczności wolą jedno słowo od drugiego, warto jednak znać alternatywy.

Aby podkreślić znaczenie ograniczania mapy, oto wersja, która jest nieco bardziej wyrozumiała dla tego, co kończy się w tej tablicy.

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

Kilka dodatkowych istotnych lektur:


11
Zgadzam się, reducemówi mi więcej o tym, co robi ta funkcja, ale injectbrzmi znacznie lepiej .
everett1992

1
Zgadzam się z ostatnim komentarzem, dałeś mi najlepszą odpowiedź.
Jerska

1
Jedyny komentarz chciałbym zrobić jest to, że reducei maptak funkcje wyższego rzędu poprzedzają MapReduce. Inspiracja działa w drugą stronę. W sensie MapReduce jest to nieco inna operacja niż zwykła redukcja funkcjonalna, mająca implikacje dla sposobu komunikowania się różnych maszyn.
acjay

Ken Iverson wprowadził operator / nazywany „operatorem redukcji” w języku programowania APL. Źródło: Iverson, Kenneth. 1962. Język programowania. Wiley. Inne źródło: „Notacja jako narzędzie myśli”, wykład ACM Turinga z 1979 r., Kenneth E. Iverson, dl.acm.org/ft_gateway.cfm?id=1283935&type=pdf
Fernando Pelliccioni

112

Alternatywnie (tylko dla porównania), jeśli masz zainstalowane Railsy (właściwie tylko ActiveSupport):

require 'activesupport'
array.sum

12
Nowsze wersje activesupport nie ładują domyślnie wszystkich rozszerzeń. Będziemy chcieli albo wymagają tylko moduł Suma: require 'active_support/core_ext/enumerable.rb'lub wymagają wszystkie aktywnego wsparcia: require 'active_support/all'. Więcej informacji na ten temat tutaj: Dokumenty API
dcashman

2
Nieważne, że activesupportjest masywny zależność do przeciągania do projektu, aby przejść od array.inject(:+)do array.sum.
meagar

1
Nitpick do skądinąd dobrego komentarza: powinien być require 'active_support/core_ext/enumerable'bez .rbprzyrostka, ponieważ jest to dodawane domyślnie.
Per Lundberg,

72

Dla Ruby> = 2.4.0 możesz używać sumz Enumerables.

[1, 2, 3, 4].sum

Klasy podstawowe typu mokeypatch są niebezpieczne. Jeśli lubisz niebezpieczeństwo i używasz starszej wersji Ruby, możesz dodać #sumdo Arrayklasy:

class Array
  def sum
    inject(0) { |sum, x| sum + x }
  end
end

1
Nie rób tego
użytkownik3467349,

@ user3467349 dlaczego?
YoTengoUnLCD

15
Monkeypatching klasy podstawowe nie jest fajny.
user3467349,

1
Chodzi mu o to, że nie musisz wykonywać Małpiej łatki dla Ruby> = 2.4, i że łatanie małp jest niebezpieczne, i że możesz teraz sumować wyliczenia natywnie, ale jest też sposób na cofnięcie funkcjonalności.
Peter H. Boling,

Odebrano, ponieważ twoja implementacja zwraca zero na pustych tablicach.
Eldritch Conundrum

45

Nowość w Ruby 2.4.0

Możesz użyć trafnie nazwanej metody Enumerable#sum. Ma wiele zalet, inject(:+)ale na końcu jest też kilka ważnych uwag.

Przykłady

Zakresy

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

Tablice

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

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

Ważna uwaga

Ta metoda nie jest równoważna z #inject(:+). Na przykład

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

Również,

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

Zobacz tę odpowiedź, aby uzyskać więcej informacji o tym, dlaczego sumtak jest.


19

Ze względu na różnorodność możesz to zrobić, jeśli tablica nie jest tablicą liczb, ale tablicą obiektów, które mają właściwości, które są liczbami (np. Ilość):

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

3
Jest to równoważne temu: array.map(&:amount).inject(0, :+). Zobacz inne odpowiedzi.
Richard Jones

4
W pewnym sensie tak. Jednak użycie mapwtedy injectwymaga dwukrotnego przejścia przez tablicę: raz, aby utworzyć nową tablicę, drugi, aby zsumować członków. Ta metoda jest nieco bardziej szczegółowa, ale także bardziej wydajna.
HashFail

Najwyraźniej nie jest to bardziej wydajne, zobacz gist.github.com/cameron-martin/b907ec43a9d8b9303bdc - przypis do komentarzy w tej odpowiedzi: stackoverflow.com/a/1538949/1028679
rmcsharry

19

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

Ruby przed 2.4 - array.inject(:+)lubarray.reduce(:+)

* Uwaga: #sumMetoda jest nowym dodatkiem do 2.4, enumerabledzięki czemu będziesz mógł używać array.sumczystego rubinu, nie tylko Railsów.


2
Ruby 2.4.0 zostało wydane dzisiaj z tą funkcją! 🎉
amoebe

@amoebe masz rację! Cieszę się, że ta przydatna funkcja jest uwzględniona.
zbierz

18

Ruby 1.8.7 to:

array.inject(0, &:+) 

Jeśli czytasz mój komentarz z 2011 roku i nadal jest on istotny, ponieważ używasz 1.8.6, zaktualizuj go!
Andrew Grimm,



5

Ruby 2.4.0 zostało wydane i ma metodę Enumerable # sum . Więc możesz to zrobić

array.sum

Przykłady z dokumentów:

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

3

Pozwala również na [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

dla tablicy z zerowymi wartościami możemy wykonać kompaktowanie, a następnie wstrzyknąć sumę ex-

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

2
array.reduce(:+)

Działa również dla zakresów ... stąd

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

1

Jeśli czujesz się golfistą, możesz to zrobić

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

Spowoduje to utworzenie ciągu „123 + 321 + 12389”, a następnie użycie funkcji eval do wykonania sumy. Jest to wyłącznie do celów golfowych , nie należy używać go we właściwym kodzie.


1

Metoda 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

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

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

Metoda 4: Gdy tablica zawiera zero i puste wartości, domyślnie, jeśli użyjesz którejkolwiek z powyższych funkcji zmniejsz, podsumuj, wstrzyknij wszystko przez

TypeError: zero nie może być zmuszone do liczby całkowitej

Możesz to przezwyciężyć,

   [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

Metoda 6: eval

Ocenia wyrażenie Ruby w ciągu.

  [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 sposoby na wykonanie sumy tablic

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

2) array.inject('+')

3) array.join('+')


0

Lub możesz wypróbować tę metodę:

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

* To działało dobrze dla mnie jako nowego programisty. Możesz dostosować zakres liczb, zmieniając wartości w []


-1

Możesz to również zrobić w prosty sposób

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.