วิธีรวมอาเรย์ของตัวเลขใน Ruby


563

ฉันมีอาร์เรย์ของจำนวนเต็ม

ตัวอย่างเช่น:

array = [123,321,12389]

มีวิธีที่ดีที่จะได้รับผลรวมของพวกเขา?

ฉันรู้แล้ว

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

จะทำงาน.


19
โปรดทราบว่า Ruby 2.4+ มีarray.sum
dawg

Ruby 2.6 ไม่ได้มีมัน ทับทิมมอบให้ทับทิมนำไปดูเหมือนว่า
Lori

1
@ ลอริอืม ลิงก์
steenslag

ขอโทษ ในเวลานั้นฉันเชื่อว่าฉันใช้ 2.6 ผิดพลาดเนื่องจากความผิดพลาดของ rbenv ในส่วนของฉัน
Lori

คำตอบ:


612

ลองสิ่งนี้:

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

ดูเอกสารที่นับไม่ได้ของ Ruby

(หมายเหตุ: 0จำเป็นต้องใช้ตัวพิมพ์ใหญ่เพื่อ0ส่งคืนอาร์เรย์ว่างเปล่าแทนnil)


317
jorney array.inject(:+)มีประสิทธิภาพมากขึ้น
ปีเตอร์

3
array.inject(:+)ดูเหมือนว่าจะทำให้เกิดปัญหาใน Ruby 1.8.6 ข้อยกเว้น "LocalJumpError: ไม่มีบล็อกที่กำหนด" อาจปรากฏขึ้น
Kamil Szot

34
ในทางรถไฟarray.sumอาจให้ผลรวมของค่าอาร์เรย์
Kamil Szot

32
ในกรณีส่วนใหญ่ฉันชอบที่จะใช้reduceซึ่งเป็นนามแฝงของinject(เหมือนในarray.reduce( :+ ))
Boris Stitnicky

3
@Boris นอกจากนี้ Rubycop จะเตือนคุณสำหรับการใช้มากกว่าinject reduce
Droogans

810

หรือลองใช้วิธี Ruby 1.9:

array.inject(0, :+)

หมายเหตุ: 0กรณีฐานเป็นสิ่งจำเป็นมิฉะนั้นnilจะถูกส่งกลับในอาร์เรย์ที่ว่างเปล่า:

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

6
ฉันจะใช้วิธีนี้เพื่อรวมคุณสมบัติจากวัตถุได้อย่างไร อาเรย์ของฉัน [product1, product2] ฉันต้องการที่จะรวม product1.price + product2.price เป็นไปได้หรือไม่ที่จะใช้ array.inject (: +)
Pablo Cantero

7
คุณสามารถใช้กลอุบายที่คล้ายกันกับวิธีการแผนที่: array.map (&: price). inject (: +)
markquezada

100
array.map(&:price).inject(0, :+)ปลอดภัยกว่านิดหน่อย มันทำให้แน่ใจว่าถ้าคุณมีรายการที่ว่างเปล่าที่คุณได้รับ0แทนศูนย์
johnf

11
ใช้ array.map (... ). inject (... ) ไม่มีประสิทธิภาพคุณจะวนซ้ำข้อมูลทั้งหมดสองครั้ง ลอง array.inject(0) { |sum, product| sum += product.price }
everett1992

4
@ everett1992 และตามที่ปรากฏไม่ได้เพิ่มประสิทธิภาพเลย การทำสองขั้นตอนนั้นเร็วขึ้นสำหรับฉัน gist.github.com/cameron-martin/b907ec43a9d8b9303bdc
Cameron Martin

290
array.reduce(0, :+)

ในขณะที่เทียบเท่ากับarray.inject(0, :+)คำว่าลดกำลังเข้าสู่พื้นถิ่นที่พบบ่อยมากขึ้นด้วยการเพิ่มขึ้นของการเขียนโปรแกรม MapReduce รุ่น

ฉีด , ลด , พับ , สะสมและการบีบอัดที่มีความหมายเหมือนกันทั้งหมดเป็นชั้นของฟังก์ชั่นพับฟังก์ชั่นพับฉันพบว่าความสม่ำเสมอของรหัสฐานของคุณสำคัญที่สุด แต่เนื่องจากชุมชนต่าง ๆ มักจะชอบมากกว่าคำเดียวมันจึงมีประโยชน์ที่จะรู้ทางเลือกอื่น

เพื่อเน้นแผนที่ลดการใช้คำฟุ่มเฟือยนี่เป็นรุ่นที่ให้อภัยสิ่งที่เกิดขึ้นในอาเรย์นั้นเล็กน้อย

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

การอ่านที่เกี่ยวข้องเพิ่มเติมบางส่วน:


11
ฉันเห็นด้วยreduceบอกฉันมากกว่าสิ่งที่ฟังก์ชั่นทำ แต่injectเสียงเย็นกว่ามาก
everett1992

1
เห็นด้วยกับความคิดเห็นล่าสุดคุณให้ฉันคำตอบที่ดีที่สุด
Jerska

1
หนึ่งความคิดเห็นของผมจะทำให้เป็นที่reduceและmapฟังก์ชั่นที่สูงกว่าการสั่งซื้อก่อนวันจริง MapReduce แรงบันดาลใจวิ่งไปทางอื่น และในแง่ของ MapReduce มันเป็นการดำเนินการที่แตกต่างจากการลดฟังก์ชันการทำงานอย่างง่ายโดยมีความหมายว่าเครื่องต่างกันอย่างไร
acjay

Ken Iverson แนะนำตัวดำเนินการ / เรียกว่า "ตัวดำเนินการลด" ในภาษาโปรแกรม APL ที่มา: Iverson, Kenneth 2505 ภาษาโปรแกรม ไวลีย์ อีกแหล่งที่มา: "สัญกรณ์เป็นเครื่องมือของความคิด", 1979 บรรยาย ACM ทัวริงรางวัล, Kenneth E. Iverson, dl.acm.org/ft_gateway.cfm?id=1283935&type=pdf
Fernando Pelliccioni

112

อีกทางเลือกหนึ่ง (สำหรับการเปรียบเทียบเท่านั้น) หากคุณติดตั้ง Rails (จริงๆแล้วเป็นแค่ ActiveSupport):

require 'activesupport'
array.sum

12
Activesupport เวอร์ชันที่ใหม่กว่าจะไม่โหลดส่วนขยายทั้งหมดตามค่าเริ่มต้น คุณจะต้องการทั้งต้องเพียงโมดูลรวม: หรือต้องการทุกการสนับสนุนการใช้งาน:require 'active_support/core_ext/enumerable.rb' require 'active_support/all'เพิ่มเติมเกี่ยวกับที่นี่: เอกสาร API
dcashman

2
ไม่เคยทราบว่าactivesupportเป็นขนาดใหญ่พึ่งพาลากเข้าไปในโครงการที่จะไปจากการarray.inject(:+) array.sum
meagar

1
Nitpick ถึงความคิดเห็นที่ดีเป็นอย่างอื่น: ควรเป็นrequire 'active_support/core_ext/enumerable'โดยไม่ต้องมี.rbคำต่อท้ายเนื่องจากมีการเพิ่มโดยนัย
ต่อ Lundberg

72

สำหรับ Ruby> = 2.4.0 คุณสามารถใช้sumจาก Enumerables

[1, 2, 3, 4].sum

มันเป็นอันตรายต่อคลาสฐาน mokeypatch หากคุณชอบอันตรายและใช้ Ruby รุ่นเก่ากว่าคุณสามารถเพิ่มลง#sumในArrayคลาสได้

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

1
โปรดอย่าทำเช่นนี้
user3467349

@ user3467349 ทำไม
YoTengoUnLCD

15
คลาสพื้นฐาน Monkeypatching ไม่ดี
user3467349

1
สิ่งที่เขากำลังทำคือคุณไม่จำเป็นต้องทำ Monkey Patch for Ruby> = 2.4 และการปะของลิงนั้นเป็นสิ่งที่อันตรายและตอนนี้คุณสามารถหาผลรวมนับได้ แต่ยังมีวิธีที่จะย้อนการทำงาน
Peter H. Boling

ลงเนื่องจากการใช้งานของคุณส่งคืนศูนย์ในอาร์เรย์ที่ว่างเปล่า
Eldritch Conundrum

45

ใหม่สำหรับ Ruby 2.4.0

Enumerable#sumคุณสามารถใช้วิธีการตั้งชื่อเหมาะเจาะ มันมีข้อดีมากกว่าinject(:+)แต่มีโน้ตสำคัญที่ต้องอ่านในตอนท้ายเช่นกัน

ตัวอย่าง

ช่วง

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

อาร์เรย์

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

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

โน๊ตสำคัญ

#inject(:+)วิธีการนี้จะไม่เทียบเท่ากับ ตัวอย่างเช่น

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

นอกจากนี้

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

ดูคำตอบนี้สำหรับข้อมูลเพิ่มเติมเกี่ยวกับสาเหตุที่sumเป็นเช่นนี้


19

เพื่อความหลากหลายคุณสามารถทำได้ถ้าอาร์เรย์ของคุณไม่ใช่อาร์เรย์ของตัวเลข แต่เป็นอาร์เรย์ของวัตถุที่มีคุณสมบัติที่เป็นตัวเลข (เช่นจำนวน):

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

3
สิ่งนี้เทียบเท่ากับการทำ: array.map(&:amount).inject(0, :+). ดูคำตอบอื่น ๆ
Richard Jones

4
ใช่แล้ว อย่างไรก็ตามการใช้mapแล้วinjectคุณจะต้องห่วงผ่านอาร์เรย์สองครั้งเพื่อสร้างอาร์เรย์ใหม่อื่น ๆ ที่จะสรุปสมาชิก วิธีนี้ค่อนข้างละเอียดกว่าเล็กน้อย แต่ก็มีประสิทธิภาพมากกว่าด้วย
HashFail

เห็นได้ชัดว่ามันไม่ได้มีประสิทธิภาพมากขึ้นดูgist.github.com/cameron-martin/b907ec43a9d8b9303bdc - ให้เครดิตกับความคิดเห็นในคำตอบนี้: stackoverflow.com/a/1538949/1028679
rmcsharry

19

Ruby 2.4+ / Rails - array.sumเช่น[1, 2, 3].sum # => 6

ทับทิมก่อน 2.4 - array.inject(:+)หรือarray.reduce(:+)

* หมายเหตุ: #sumวิธีนี้เป็นส่วนเสริมใหม่สำหรับ 2.4 enumerableดังนั้นตอนนี้คุณจะสามารถใช้array.sumในทับทิมบริสุทธิ์ไม่ใช่แค่ Rails


2
Ruby 2.4.0 เปิดตัวแล้ววันนี้พร้อมคุณสมบัตินี้! 🎉
amoebe

@ amoebe คุณถูกต้อง! ดีใจที่ได้เห็นคุณสมบัติที่มีประโยชน์นี้รวมอยู่ด้วย
รวบรวม

18

ruby 1.8.7 way เป็นดังต่อไปนี้:

array.inject(0, &:+) 

หากคุณอ่านความคิดเห็นของฉันในปี 2011 และยังคงมีความเกี่ยวข้องขณะที่คุณใช้ 1.8.6 โปรดอัปเกรด
Andrew Grimm

16

คุณสามารถใช้:

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

ทำไมงานนี้inject(:+)แต่นี้ไม่ได้inject :+?
Arnold Roa

@ArnoldRoa "inject: +" มันเหมาะกับฉันคุณได้ผลลัพธ์อะไร
Ganesh Sagare


5

ทับทิม 2.4.0 ถูกปล่อยออกมาและมันก็มี# รวม Enumerableวิธี ดังนั้นคุณสามารถทำได้

array.sum

ตัวอย่างจากเอกสาร:

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

3

ยังอนุญาตให้[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

สำหรับอาร์เรย์ที่มีค่าศูนย์เราสามารถทำขนาดกะทัดรัดแล้วฉีดผลรวม

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


1

ถ้าคุณรู้สึกว่ากอล์ฟคุณสามารถทำได้

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

สิ่งนี้จะสร้างสตริง "123 + 321 + 12389" จากนั้นใช้ฟังก์ชัน eval เพื่อหาผลรวม นี่เป็นเพียงการเล่นกอล์ฟเท่านั้นคุณไม่ควรใช้รหัสนี้อย่างถูกต้อง


1

วิธีที่ 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

วิธีที่ 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 `+'

วิธีที่ 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 `+'

วิธีที่ 4: เมื่อ Array มีค่าศูนย์และค่าว่างโดยค่าเริ่มต้นหากคุณใช้ฟังก์ชันใด ๆ ข้างต้นลด, ผลรวม, ฉีดทุกอย่างจะผ่าน

TypeError: ไม่มีค่าบังคับใน Integer

คุณสามารถเอาชนะสิ่งนี้ได้โดย

   [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

วิธีที่ 6: eval

หาค่านิพจน์ทับทิมในสตริง

  [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 วิธีที่เราสามารถทำผลรวมของอาร์เรย์

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

2) array.inject('+')

3) array.join('+')




0
number = [1..100]

number. each do |n|

    final_number = n.sum

    puts "The sum is #{final_number}"
end

* สิ่งนี้ทำงานได้ดีสำหรับฉันในฐานะนักพัฒนาใหม่ คุณสามารถปรับช่วงตัวเลขของคุณโดยการเปลี่ยนค่าภายใน []



-8

คุณสามารถใช้. mapและ. sum ได้เช่น:

array.map { |e| e }.sum

3
แผนที่ทำอะไรที่ส่งคืนองค์ประกอบเดียวกัน นี่เป็นสิ่งเดียวกันมากกว่าarray.sum
Arnold Roa

นอกจากนี้ array.sum ไม่มีอยู่ในทับทิม ดูคำตอบ Mike Woodhouse
Ulysse BN

ตอนนี้ใช้งานใน Ruby 2.4.0
installero
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.