Bagaimana cara menjumlahkan array angka di Ruby?

563

Saya memiliki array bilangan bulat.

Sebagai contoh:

array = [123,321,12389]

Apakah ada cara yang bagus untuk mendapatkan jumlah mereka?

Saya tahu itu

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

akan bekerja.

brainfck
sumber
19
Harap dicatat bahwa Ruby 2.4+ memilikiarray.sum
dawg
Ruby 2.6 tidak memilikinya. Ruby memberi, Ruby mengambilnya, tampaknya.
Lori
1
@Lori hmm? tautan
steenslag
Maaf. Pada saat itu saya keliru meyakini bahwa saya menggunakan 2.6 karena kesalahan pada bagian saya.
Lori

Jawaban:

612

Coba ini:

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

Lihat Dokumentasi Enumerable Ruby

(catatan: 0case dasar diperlukan sehingga 0akan dikembalikan pada array kosong bukan nil)

zenazn
sumber
317
jorney array.inject(:+)lebih efisien.
Peter
3
array.inject(:+)tampaknya menyebabkan masalah di Ruby 1.8.6 Pengecualian "LocalJumpError: no block diberikan" mungkin muncul.
Kamil Szot
34
In rails array.summungkin memberi Anda jumlah nilai array.
Kamil Szot
32
Dalam kebanyakan kasus, saya lebih suka menggunakan reduce, yang merupakan alias dari inject(seperti pada array.reduce( :+ )).
Boris Stitnicky
3
@Boris Juga, Rubycop akan memperingatkan Anda untuk menggunakan injectdaripada reduce.
Droogan
810

Atau coba cara Ruby 1.9:

array.inject(0, :+)

Catatan: 0case dasar diperlukan jika tidak nilakan dikembalikan pada array kosong:

> [].inject(:+)
nil
> [].inject(0, :+)
0
jomey
sumber
6
Bagaimana saya bisa menggunakan cara ini untuk menjumlahkan atribut dari objek. Array saya [product1, product2] Saya ingin menjumlahkan product1.price + product2.price. Apakah mungkin menggunakan array.inject (: +)?
Pablo Cantero
7
Anda dapat menggunakan trik serupa dengan metode peta: array.map (&: price) .inject (: +)
markquezada
100
array.map(&:price).inject(0, :+)sedikit lebih aman. Itu memastikan bahwa jika Anda memiliki daftar kosong Anda mendapatkan 0 bukannya nihil .
johnf
11
menggunakan array.map (...). inject (...) tidak efisien, Anda akan mengulangi semua data dua kali. Coba array.inject(0) { |sum, product| sum += product.price }
everett1992
4
@ everett1992 dan ternyata, bahkan tidak ada optimasi sama sekali. Melakukannya dalam dua tahap secara konsisten lebih cepat bagi saya. gist.github.com/cameron-martin/b907ec43a9d8b9303bdc
Cameron Martin
290
array.reduce(0, :+)

Sementara setara dengan array.inject(0, :+), pengurangan kata memasuki bahasa yang lebih umum dengan munculnya model pemrograman MapReduce .

menyuntikkan , mengurangi , melipat , menumpuk , dan mengompres semuanya identik sebagai kelas fungsi lipat . Saya menemukan konsistensi di seluruh basis kode Anda yang paling penting, tetapi karena berbagai komunitas cenderung lebih suka satu kata dari yang lain, tetap berguna untuk mengetahui alternatifnya.

Untuk menekankan verbiage pengurangan peta, berikut adalah versi yang sedikit lebih memaafkan pada apa yang berakhir di array itu.

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

Beberapa bacaan tambahan yang relevan:

Evan
sumber
11
Saya setuju, reduceceritakan lebih banyak tentang apa fungsinya, tetapi injectterdengar lebih keren.
everett1992
1
Setuju dengan komentar terakhir, Anda memberi saya jawaban terbaik.
Jerska
1
Satu komentar yang akan saya sampaikan adalah bahwa reducedan mapketika fungsi tingkat tinggi mendahului MapReduce. Inspirasi berjalan sebaliknya. Dan dalam pengertian MapReduce, ini adalah operasi yang agak berbeda dari pengurangan fungsional sederhana, memiliki implikasi untuk bagaimana mesin yang berbeda berkomunikasi.
acjay
Ken Iverson memperkenalkan operator / disebut "operator reduksi" dalam bahasa pemrograman APL. Sumber: Iverson, Kenneth. 1962. Bahasa Pemrograman. Wiley. Sumber lain: "Notasi sebagai Alat Pemikiran", 1979 ACM Turing Award Lecture, Kenneth E. Iverson, dl.acm.org/ft_gateway.cfm?id=1283935&type=pdf
Fernando Pelliccioni
112

Atau (hanya untuk perbandingan), jika Anda memasang Rails (sebenarnya hanya ActiveSupport):

require 'activesupport'
array.sum
Mike Woodhouse
sumber
12
Versi activesupport yang lebih baru sebenarnya tidak memuat semua ekstensi secara default. Anda akan ingin baik memerlukan hanya jumlah modul: require 'active_support/core_ext/enumerable.rb', atau memerlukan semua dukungan aktif: require 'active_support/all'. Lebih lanjut tentang ini di sini: API Documents
dcashman
2
Tak peduli bahwa activesupportadalah besar ketergantungan untuk tarik ke sebuah proyek untuk pergi dari array.inject(:+)ke array.sum.
meagar
1
Nitpick komentar yang baik: seharusnya require 'active_support/core_ext/enumerable'tanpa .rbakhiran, karena ditambahkan secara implisit.
Per Lundberg
72

Untuk Ruby> = 2.4.0 Anda dapat menggunakan sumdari Enumerables.

[1, 2, 3, 4].sum

Berbahaya untuk mokeypatch kelas dasar. Jika Anda suka bahaya dan menggunakan versi Ruby yang lebih lama, Anda bisa menambahkan #sumke Arraykelas:

class Array
  def sum
    inject(0) { |sum, x| sum + x }
  end
end
Jrhicks
sumber
1
Tolong jangan lakukan ini
user3467349
@ user3467349 mengapa?
YoTengoUnLCD
15
Kelas dasar monkeypatching tidak baik.
user3467349
1
Maksudnya adalah bahwa Anda tidak perlu melakukan Patch Monyet untuk Ruby> = 2.4, dan bahwa patch monyet berbahaya, dan bahwa Anda sekarang dapat menjumlahkan enumerable secara native, tetapi ada juga cara untuk mendukung fungsi tersebut.
Peter H. Boling
Diturunkan karena implementasi Anda mengembalikan nol pada array kosong.
Eldritch Conundrum
45

Baru untuk Ruby 2.4.0

Anda dapat menggunakan metode yang dinamai tepat Enumerable#sum. Ini memiliki banyak keunggulan dibandingkan inject(:+)tetapi ada beberapa catatan penting untuk dibaca di akhir juga.

Contohnya

Kisaran

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

Array

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

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

Catatan penting

Metode ini tidak setara dengan #inject(:+). Sebagai contoh

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

Juga,

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

Lihat jawaban ini untuk informasi lebih lanjut tentang mengapa sumseperti ini.

Eli Sadoff
sumber
20

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

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

* Catatan: #sumMetode ini merupakan tambahan baru untuk 2.4 enumerablesehingga Anda sekarang dapat menggunakan array.sumruby murni, bukan hanya Rails.

mengumpulkan
sumber
2
Ruby 2.4.0 dirilis hari ini dengan fitur ini disertakan! 🎉
amoebe
@amoebe kamu benar! Senang melihat fitur bermanfaat ini dimasukkan.
kumpulkan
19

Hanya demi keberagaman, Anda juga dapat melakukan ini jika array Anda bukan array angka, melainkan array objek yang memiliki properti yang berupa angka (misalnya jumlah):

array.inject(0){|sum,x| sum + x.amount}
HashFail
sumber
3
Ini sama dengan melakukan: array.map(&:amount).inject(0, :+). Lihat jawaban lain.
Richard Jones
4
Di satu sisi, ya. Namun, menggunakan mapkemudian injectmengharuskan Anda untuk mengulang array dua kali: sekali untuk membuat array baru, yang lain untuk menjumlahkan anggota. Metode ini sedikit lebih verbose, tetapi juga lebih efisien.
HashFail
Tampaknya itu tidak lebih efisien, lihat gist.github.com/cameron-martin/b907ec43a9d8b9303bdc - kredit untuk komentar dalam jawaban ini: stackoverflow.com/a/1538949/1028679
rmcsharry
18

Cara ruby ​​1.8.7 adalah sebagai berikut:

array.inject(0, &:+) 
Vova
sumber
Jika Anda membaca komentar 2011 saya, dan masih relevan saat Anda menggunakan 1.8.6, harap tingkatkan!
Andrew Grimm
16

Anda cukup menggunakan:

    example = [1,2,3]
    example.inject(:+)
Ganesh Sagare
sumber
Mengapa ini berhasil: inject(:+)tetapi ini tidak inject :+?
Arnold Roa
@ArnoldRoa "menyuntikkan: +" ini berfungsi untuk saya, hasil apa yang Anda dapatkan?
Ganesh Sagare
6

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

Mahesh Bablu
sumber
5

Ruby 2.4.0 dirilis, dan ia memiliki metode # jumlah Enumerable . Jadi kamu bisa melakukannya

array.sum

Contoh dari dokumen:

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

Juga memungkinkan untuk [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
lebih kotor
sumber
3

untuk array dengan nilai nil kita bisa melakukan compact dan kemudian menyuntikkan jumlah ex

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

Bekerja untuk Ranges juga ... karenanya,

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

Jika Anda merasa golf, Anda bisa melakukannya

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

Ini akan membuat string "123 + 321 + 12389" dan kemudian gunakan fungsi eval untuk melakukan penjumlahan. Ini hanya untuk tujuan bermain golf , Anda tidak boleh menggunakannya dalam kode yang tepat.

Ulysse BN
sumber
1

Metode 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

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

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

Metode 4: Ketika Array berisi nihil dan nilai kosong, secara default jika Anda menggunakan fungsi di atas mengurangi, menjumlahkan, menyuntikkan semuanya akan melalui

TypeError: nil tidak bisa dipaksa menjadi Integer

Anda dapat mengatasi ini dengan,

   [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

Metode 6: eval

Mengevaluasi ekspresi Ruby dalam string.

  [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
sumber
1

3 cara yang bisa kita lakukan jumlah array

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

2) array.inject('+')

3) array.join('+')

Poonkodi
sumber
0

Atau Anda dapat mencoba metode ini:

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

Ini jalan terpendek. Cobalah.

array.inject :+

Tej Poudel
sumber
0
number = [1..100]

number. each do |n|

    final_number = n.sum

    puts "The sum is #{final_number}"
end

* Ini bekerja dengan baik untuk saya sebagai pengembang baru. Anda dapat menyesuaikan rentang angka dengan mengubah nilai dalam []

Madeline Young
sumber
-1

Anda juga bisa melakukannya dengan mudah

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

Anda dapat menggunakan .map dan .sum seperti:

array.map { |e| e }.sum
shabdar
sumber
3
Apa gunanya peta mengembalikan elemen yang sama? ini persis sama denganarray.sum
Arnold Roa
Lagipula array.sum tidak ada di ruby. Lihat jawaban Mike Woodhouse
Ulysse BN
Itu sekarang di Ruby 2.4.0
installero