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.


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)


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

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:


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

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

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.


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.


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}

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

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

Mengapa ini berhasil: inject(:+)tetapi ini tidak inject :+?
Arnold Roa

@ArnoldRoa "menyuntikkan: +" ini berfungsi untuk saya, hasil apa yang Anda dapatkan?
Ganesh Sagare


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

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

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

2
array.reduce(:+)

Bekerja untuk Ranges juga ... karenanya,

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

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.


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+

1

3 cara yang bisa kita lakukan jumlah array

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

2) array.inject('+')

3) array.join('+')


0

Atau Anda dapat mencoba metode ini:

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

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


-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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.