Ruby'deki sayı dizisini nasıl toplayabilirim?


563

Bir tamsayı dizim var.

Örneğin:

array = [123,321,12389]

Bunların toplamını almanın güzel bir yolu var mı?

Bunu biliyorum

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

çalışır.


19
Lütfen dikkat: Ruby 2.4+array.sum
dawg

Ruby 2.6'da yok. Ruby artar, Ruby taketh uzakta, öyle görünüyor.
Lori

1
@Lori hmm? bağlantı
steenslag

Afedersiniz. O zaman yanımda bir rbenv kayması nedeniyle 2.6 kullandığımı yanlışlıkla düşündüm.
Lori

Yanıtlar:


612

Bunu dene:

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

Ruby'nin Numaralandırılabilir Belgelerine Bakın

(not: 0temel durum gereklidir, böylece 0bunun yerine boş bir dizide döndürülür nil)


317
jorney's array.inject(:+)daha verimlidir.
Peter

3
array.inject(:+)Ruby'de sorun yaratıyor gibi görünüyor 1.8.6 İstisnalar "LocalJumpError: verilen blok yok" belirebilir.
Kamil Szot

34
Raylarda array.sumdizi değerlerinin toplamını verebilir.
Kamil Szot

32
Çoğu durumda, (olduğu gibi ) reducebir takma adı olan kullanmayı tercih ederim . injectarray.reduce( :+ )
Boris Stitnicky

3
@Boris Ayrıca, Rubycop kullanmak injectyerine sizi uyarır reduce.
Droogans

810

Veya Ruby 1.9 yolunu deneyin:

array.inject(0, :+)

Not: 0temel kasa gereklidir aksi takdirde nilboş dizilerde iade edilir:

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

6
Nesneden bir özniteliği toplamak için bu yolu nasıl kullanabilirim. Dizim [product1, product2] product1.price + product2.price öğelerini toplamak istiyorum. Array.inject (: +) kullanmak mümkün mü?
Pablo Cantero

7
Harita yöntemiyle benzer bir numara kullanabilirsiniz: array.map (&: price) .inject (: +)
markquezada

100
array.map(&:price).inject(0, :+)biraz daha güvenlidir. Boş bir listeniz varsa nil yerine 0 almanızı sağlar .
johnf

11
array.map (...). kullanarak inject (...) verimsizse, tüm verileri iki kez yineleyeceksiniz. Deneyin array.inject(0) { |sum, product| sum += product.price }
everett1992

4
@ everett1992 ve ortaya çıktığı gibi, bir optimizasyon bile değil. İki aşamada yapmak benim için sürekli daha hızlı. gist.github.com/cameron-martin/b907ec43a9d8b9303bdc
Cameron Martin

290
array.reduce(0, :+)

Buna eşit olmakla birlikte array.inject(0, :+), azaltma terimi MapReduce programlama modellerinin yükselişiyle daha yaygın bir yerel dile giriyor .

enjekte etme , azaltma , katlama , biriktirme ve sıkıştırma , katlama işlevleri sınıfı olarak eşanlamlıdır . Kod tabanınızdaki tutarlılığı en önemli buluyorum, ancak çeşitli topluluklar bir kelimeyi diğerine tercih etme eğiliminde olduğundan, alternatifleri bilmek yararlıdır.

Harita azaltma ayrıntılarını vurgulamak için, bu dizide neyin bittiği konusunda biraz daha affedici bir sürüm.

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

İlgili ek okumalar:


11
Katılıyorum, reducebana işlevin ne yaptığından daha fazlasını anlatıyor, ama injectkulağa daha havalı geliyor.
everett1992

1
Son yorumla aynı fikirde, bana en iyi yanıtı verdin.
Jerska

1
Yapacağım tek yorum şudur reduceve mapüst düzey işlevler MapReduce'dan önce gelir. İlham başka bir şekilde ilerliyor. MapReduce anlamında, farklı makinelerin nasıl iletişim kurdukları üzerinde çıkarımları olan basit bir işlevsel azaltma işleminden biraz farklı bir işlemdir.
acjay

Ken Iverson APL programlama dilinde operatörü / "azaltma operatörü" olarak adlandırdı. Kaynak: Iverson, Kenneth. 1962. Bir Programlama Dili. Wiley. Başka bir kaynak: "Bir Düşünce Aracı Olarak Gösterim", 1979 ACM Turing Ödülü Konferansı, Kenneth E. Iverson, dl.acm.org/ft_gateway.cfm?id=1283935&type=pdf
Fernando Pelliccioni

112

Alternatif olarak (yalnızca karşılaştırma için), Rails yüklüyse (aslında sadece ActiveSupport):

require 'activesupport'
array.sum

12
Activesupport'un daha yeni sürümleri varsayılan olarak tüm uzantıları yüklemez. Ya sadece toplamı modülü gerektiren istersiniz: require 'active_support/core_ext/enumerable.rb'ya da aktif desteğinin tüm gerektirir: require 'active_support/all'. Daha fazlası için: API Docs
dcashman 21:12

2
Bunun bir projeye sürüklenmek activesupportiçin büyük bir bağımlılık olduğunu aklınızdan array.inject(:+)çıkarmayın array.sum.
meagar

1
Nitpick, aksi halde iyi bir yoruma yorum yapar: bu , örtük olarak eklendiğinden require 'active_support/core_ext/enumerable', .rbsonek olmadan olmalıdır .
Per Lundberg

72

Ruby> = 2.4.0 sumiçin Numaralandırıcılar'dan kullanabilirsiniz .

[1, 2, 3, 4].sum

Mokeypatch temel sınıfları tehlikelidir. Eğer tehlikeyi ve Ruby eski bir sürümünü kullanıyor isterseniz, ekleyebilirsiniz #sumiçin Arraysınıfın:

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

1
Lütfen bunu yapma
user3467349

@ user3467349 neden?
YoTengoUnLCD

15
Monkeypatching temel sınıfları hoş değil.
user3467349

1
Yaptığı nokta, Ruby için Maymun Yaması> 2.4 yapmanıza gerek yok ve bu maymun yaması tehlikelidir ve artık numaralandırılabilir olanları yerel olarak toplayabilmenizdir, ancak işlevselliği desteklemenin bir yolu da vardır.
Peter H. Boling

Uygulamanız boş dizilerde sıfır döndürdüğü için indirildi.
Eldritch Conundrum

45

Ruby 2.4.0'daki yenilikler

Uygun şekilde adlandırılmış yöntemi kullanabilirsiniz Enumerable#sum. Çok fazla avantajı var inject(:+)ama sonunda okunması gereken bazı önemli notlar da var.

Örnekler

aralıklar

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

Diziler

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

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

Önemli Not

Bu yöntem eşdeğer değildir #inject(:+). Örneğin

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

Ayrıca,

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

Neden böyle olduğu hakkında daha fazla bilgi için bu cevaba bakınız sum.


20

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

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

* Not: #sumYöntem 2.4 için yeni bir ektir, enumerablebu yüzden artık array.sumsadece Rails değil, saf yakutta da kullanabileceksiniz .


2
Ruby 2.4.0 bugün bu özellik dahil olarak piyasaya sürüldü! 🎉
amoebe

@ amobe haklısın! Bu kullanışlı özelliği görmek sevindim.
toplamak

19

Sadece çeşitlilik uğruna, diziniz bir sayı dizisi değil, sayı olan özelliklere sahip nesneler dizisi (örneğin miktar) ise bunu da yapabilirsiniz:

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

3
Bu yapmakla eşdeğerdir: array.map(&:amount).inject(0, :+). Diğer cevaplara bakın.
Richard Jones

4
Bir bakıma, evet. Ancak, mapdaha sonra kullanmak injectdizi boyunca iki kez döngü yapmanızı gerektirir: bir kez yeni bir dizi oluşturmak için, diğeri üyeleri toplamak için. Bu yöntem biraz daha ayrıntılı, ancak aynı zamanda daha verimlidir.
HashFail

Görünüşe göre daha verimli değil, bkz. Gist.github.com/cameron-martin/b907ec43a9d8b9303bdc - bu
yanıttaki

18

yakut 1.8.7 yolu aşağıdaki gibidir:

array.inject(0, &:+) 

2011 yorumumu okuduysanız ve 1.8.6 kullandığınız için hala alakalı ise, lütfen yeni sürüme geçin!
Andrew Grimm

16

Sadece şunu kullanabilirsiniz:

    example = [1,2,3]
    example.inject(:+)

Neden işe yarıyor: inject(:+)ama bu işe yaramıyor inject :+?
Arnold Roa

@ArnoldRoa "enjekte: +" benim için çalışıyor, ne sonuç aldınız?
Ganesh Sagare


5

Ruby 2.4.0 yayınlandı ve Enumerable # sum yöntemi var. Böylece yapabilirsin

array.sum

Dokümanlardan örnekler:

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

3

Ayrıca [1,2].sum{|x| x * 2 } == 6şunları sağlar :

# 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

sıfır değerli diziler için kompakt yapabiliriz ve sonra toplamı enjekte edebiliriz.

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

2
array.reduce(:+)

İçin çalışır Aralıkları da ... dolayısıyla

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

1

Golf gibi hissediyorsanız,

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

Bu, bir "123 + 321 + 12389" dizesi oluşturur ve daha sonra toplamı yapmak için function eval işlevini kullanır. Bu sadece golf amaçlıdır , uygun kodda kullanmamalısınız.


1

Yöntem 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

Yöntem 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 `+'

Yöntem 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 `+'

Yöntem 4: Dizi nil ve boş değerler içerdiğinde, varsayılan olarak yukarıdaki işlevleri kullanırsanız, her şeyi kullanarak

TypeError: nil Tamsayıya zorlanamaz

Bunun üstesinden gelebilirsiniz,

   [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

Yöntem 6: Eval

Dize içindeki Ruby ifadelerini değerlendirir.

  [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

Dizi toplamı yapmanın 3 yolu

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

2) array.inject('+')

3) array.join('+')


0

Veya bu yöntemi deneyebilirsiniz:

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

* Bu yeni bir geliştirici olarak benim için iyi çalıştı. Sayı aralığınızı [] içindeki değerleri değiştirerek ayarlayabilirsiniz


-1

Kolayca da yapabilirsiniz

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.