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.

brainfck
źródło
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)

zenazn
źródło
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
jomey
źródło
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:

Evan
źródło
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
Mike Woodhouse
źródło
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
jrhicks
źródło
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.

Eli Sadoff
źródło
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}
HashFail
źródło
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.

zebrać
źródło
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, &:+) 
Wowa
źródło
Jeśli czytasz mój komentarz z 2011 roku i nadal jest on istotny, ponieważ używasz 1.8.6, zaktualizuj go!
Andrew Grimm,
16

Możesz po prostu użyć:

    example = [1,2,3]
    example.inject(:+)
Ganesh Sagare
źródło
Dlaczego to działa: inject(:+)ale to nie działa inject :+?
Arnold Roa,
@ArnoldRoa „inject: +” działa dla mnie, jaki wynik uzyskałeś?
Ganesh Sagare
6

Wystarczy [1,2,3].inject('+')

Mahesh Bablu
źródło
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
Santhosh
źródło
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
grosser
źródło
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(:+)
thedudecodes
źródło
2
array.reduce(:+)

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

(1..10).reduce(:+) returns 55
MulleOne
źródło
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.

Ulysse BN
źródło
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+
Nataraja B.
źródło
1

3 sposoby na wykonanie sumy tablic

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

2) array.inject('+')

3) array.join('+')

Poonkodi
źródło
0

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

def sum arr
  0 if arr.empty
  arr.inject :+
end
ramin
źródło
0

To jest najkrótsza droga. Spróbuj.

array.inject :+

Tej Poudel
źródło
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 []

Madeline Young
źródło
-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
Prabhakar Undurthi
źródło
-8

Możesz użyć .map i .sum, takich jak:

array.map { |e| e }.sum
shabdar
źródło
3
Po co mapa zwraca ten sam element? jest to dokładnie to samo, coarray.sum
Arnold Roa,
Co więcej, array.sum nie istnieje w Rubim. Zobacz odpowiedź Mike'a Woodhouse'a
Ulysse BN
Działa teraz w Ruby 2.4.0
installero