Làm thế nào để tổng hợp các mảng số trong Ruby?


563

Tôi có một loạt các số nguyên.

Ví dụ:

array = [123,321,12389]

Có cách nào hay để lấy tổng của chúng không?

Tôi biết điều đó

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

sẽ làm việc


19
Xin lưu ý rằng Ruby 2.4+ cóarray.sum
dawg

Ruby 2.6 không có nó. Ruby giveth, dường như Ruby đi.
Lori

1
@Lori hmm? liên kết
steenslag

Lấy làm tiếc. Vào thời điểm đó, tôi đã lầm tưởng rằng tôi đang sử dụng 2.6 vì phần rbenv bị trượt.
Lori

Câu trả lời:


612

Thử cái này:

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

Xem tài liệu vô số của Ruby

(lưu ý: 0trường hợp cơ sở là cần thiết để nó 0sẽ được trả về trên một mảng trống thay vì nil)


317
luật sư array.inject(:+)là hiệu quả hơn.
Peter

3
array.inject(:+)dường như gây ra sự cố trong Ruby 1.8.6 Ngoại lệ "LocalJumpError: không có khối nào được đưa ra" có thể bật lên.
Kamil Szot

34
Trong đường ray array.sumcó thể cung cấp cho bạn tổng của các giá trị mảng.
Kamil Szot

32
Trong hầu hết các trường hợp, tôi thích sử dụng reduce, đó là bí danh của inject(như trong array.reduce( :+ )).
Boris Stitnicky

3
@Boris Ngoài ra, Rubycop sẽ cảnh báo bạn về việc sử dụng injectchứ không phải reduce.
Giọt nước

810

Hoặc thử cách Ruby 1.9:

array.inject(0, :+)

Lưu ý: 0trường hợp cơ sở là cần thiết nếu không nilsẽ được trả về trên các mảng trống:

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

6
Làm thế nào tôi có thể sử dụng cách này để tổng hợp một thuộc tính từ đối tượng. Mảng của tôi [sản phẩm1, sản phẩm2] Tôi muốn tính tổng sản phẩm1.price + sản ​​phẩm2.price. Có thể sử dụng mảng.inject (: +) không?
Pablo Cantero

7
Bạn có thể sử dụng một mẹo tương tự với phương thức bản đồ: Array.map (&: price) .inject (: +)
markquezada

100
array.map(&:price).inject(0, :+)an toàn hơn một chút Nó đảm bảo rằng nếu bạn có một danh sách trống, bạn nhận được 0 thay vì không .
johnf

11
sử dụng mảng.map (...). chích (...) không hiệu quả, bạn sẽ lặp qua tất cả dữ liệu hai lần. Hãy thử array.inject(0) { |sum, product| sum += product.price }
everett1992

4
@ everett1992 và khi nó bật ra, thậm chí không tối ưu hóa chút nào. Làm điều đó trong hai giai đoạn là nhanh hơn đối với tôi. gist.github.com/cameron-martin/b907ec43a9d8b9303bdc
Cameron Martin

290
array.reduce(0, :+)

Trong khi tương đương với array.inject(0, :+), thuật ngữ giảm đang bước vào một ngôn ngữ phổ biến hơn với sự gia tăng của các mô hình lập trình MapReduce .

tiêm , giảm , gấp , tích lũynén đều đồng nghĩa như một lớp các hàm gấp . Tôi thấy tính nhất quán trong cơ sở mã của bạn là quan trọng nhất, nhưng vì các cộng đồng khác nhau có xu hướng thích một từ hơn một từ khác, dù sao cũng hữu ích khi biết các lựa chọn thay thế.

Để nhấn mạnh thông số giảm bản đồ, đây là phiên bản dễ tha thứ hơn một chút về những gì kết thúc trong mảng đó.

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

Một số bài đọc liên quan bổ sung:


11
Tôi đồng ý, reducecho tôi biết nhiều hơn những gì chức năng làm, nhưng injectâm thanh mát hơn nhiều.
everett1992

1
Đồng ý với nhận xét cuối cùng, bạn đã cho tôi câu trả lời tốt nhất.
Jerska 11/11/13

1
Một nhận xét tôi sẽ đưa ra là reducemapkhi các hàm bậc cao hơn có trước MapReduce. Cảm hứng chạy theo cách khác. Và theo nghĩa MapReduce, đó là một hoạt động hơi khác so với giảm chức năng đơn giản, có ý nghĩa đối với cách các máy khác nhau giao tiếp.
acjay

Ken Iverson đã giới thiệu toán tử / được gọi là "toán tử rút gọn" trong ngôn ngữ lập trình APL. Nguồn: Iverson, Kenneth. 1962. Một ngôn ngữ lập trình. Wiley. Một nguồn khác: "Ký hiệu như một công cụ của suy nghĩ", Bài giảng Giải thưởng ACM Turing 1979, Kenneth E. Iverson, dl.acm.org/ft_gateway.cfm?id=1283935&type=pdf
Fernando Pelliccioni

112

Ngoài ra (chỉ để so sánh), nếu bạn đã cài đặt Rails (thực ra chỉ là ActiveSupport):

require 'activesupport'
array.sum

12
Các phiên bản mới hơn của hỗ trợ kích hoạt không thực sự tải tất cả các tiện ích mở rộng theo mặc định. Bạn sẽ muốn chỉ yêu cầu mô-đun tổng : require 'active_support/core_ext/enumerable.rb', hoặc yêu cầu tất cả hỗ trợ tích cực : require 'active_support/all'. Thông tin thêm về nó ở đây: API Docs
dcashman 21/03 '

2
Đừng bận tâm rằng activesupportlà một đồ sộ phụ thuộc để kéo vào một dự án để đi từ array.inject(:+)đến array.sum.
meagar

1
Nitpick cho một bình luận tốt khác: nó nên require 'active_support/core_ext/enumerable'không có .rbhậu tố, vì đó được thêm vào ngầm.
Per Lundberg

72

Đối với Ruby> = 2.4.0, bạn có thể sử dụng sumtừ Enumerables.

[1, 2, 3, 4].sum

Nó là nguy hiểm cho các lớp cơ sở mokeypatch. Nếu bạn thích nguy hiểm và sử dụng phiên bản cũ hơn của Ruby, bạn có thể thêm #sumvào Arraylớp:

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

1
Vui lòng không làm điều này
user3467349

@ user3467349 tại sao?
YoTengoUnLCD

15
Các lớp cơ sở không phù hợp là không tốt.
dùng3467349

1
Điểm anh ấy đang đưa ra là bạn không cần phải thực hiện Monkey Patch cho Ruby> = 2.4, và việc vá khỉ rất nguy hiểm, và giờ đây bạn có thể tổng hợp vô số thứ tự, nhưng cũng có một cách để nhập lại chức năng.
Peter H. Boling

Bị từ chối vì việc triển khai của bạn trả về con số không trên các mảng trống.
Câu hỏi hóc búa Eldritch

45

Mới cho Ruby 2.4.0

Bạn có thể sử dụng phương pháp được đặt tên khéo léo Enumerable#sum. Nó có rất nhiều lợi thế hơn inject(:+)nhưng cũng có một số lưu ý quan trọng để đọc ở cuối.

Ví dụ

Các dãy

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

Mảng

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

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

Lưu ý quan trọng

Phương pháp này không tương đương với #inject(:+). Ví dụ

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

Cũng thế,

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

Xem câu trả lời này để biết thêm thông tin về lý do tại sao sumnhư thế này.


20

Ruby 2.4+ / Rails - array.sumtức là[1, 2, 3].sum # => 6

Ruby trước 2.4 - array.inject(:+)hoặcarray.reduce(:+)

* Lưu ý: #sumPhương pháp này là một bổ sung mới cho 2.4 enumerablevì vậy bây giờ bạn sẽ có thể sử dụng array.sumtrong ruby ​​nguyên chất, không chỉ Rails.


2
Ruby 2.4.0 đã được phát hành ngày hôm nay với tính năng này đi kèm! 🎉
amoebe

@amoebe bạn đúng rồi! Vui mừng khi thấy tính năng hữu ích này bao gồm.
thu thập

19

Chỉ vì mục đích đa dạng, bạn cũng có thể làm điều này nếu mảng của bạn không phải là một mảng số, mà là một mảng các đối tượng có các thuộc tính là số (ví dụ: số lượng):

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

3
Điều này tương đương với làm : array.map(&:amount).inject(0, :+). Xem câu trả lời khác.
Richard Jones

4
Theo một cách nào đó, vâng. Tuy nhiên, sử dụng mapsau đó injectyêu cầu bạn lặp qua mảng hai lần: một lần để tạo một mảng mới, lần khác để tổng hợp các thành viên. Phương pháp này dài dòng hơn một chút, nhưng cũng hiệu quả hơn.
HashFail

Rõ ràng là nó không hiệu quả hơn, xem gist.github.com/cameron-martin/b907ec43a9d8b9303bdc - ghi có vào các bình luận trong câu trả lời này: stackoverflow.com/a/1538949/1028679
rmcsharry

18

Cách ruby ​​1.8.7 là như sau:

array.inject(0, &:+) 

Nếu bạn đọc nhận xét năm 2011 của tôi và nó vẫn phù hợp vì bạn đang sử dụng 1.8.6, vui lòng nâng cấp!
Andrew Grimm

16

Bạn chỉ có thể sử dụng:

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

Tại sao điều này hoạt động: inject(:+)nhưng điều này không inject :+?
Arnold Roa

@ArnoldRoa "chích: +" nó hoạt động với tôi, bạn đã nhận được kết quả gì?
Ganesh Sagare


5

Ruby 2.4.0 được phát hành và nó có phương thức # sum . Vì vậy bạn có thể làm

array.sum

Ví dụ từ các tài liệu:

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

3

Cũng cho phép [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

đối với mảng có giá trị nil, chúng ta có thể thực hiện nén và sau đó thêm tổng ex-

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

2
array.reduce(:+)

Hoạt động cho Phạm vi quá ... do đó,

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

1

Nếu bạn cảm thấy golf, bạn có thể làm

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

Điều này sẽ tạo ra một chuỗi "123 + 321 + 12389" và sau đó sử dụng hàm eval để thực hiện tổng. Điều này chỉ dành cho mục đích chơi gôn , bạn không nên sử dụng nó trong mã thích hợp.


1

Cách 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

Cách 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 `+'

Cách 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 `+'

Phương pháp 4: Khi Mảng chứa một giá trị nil và rỗng, theo mặc định, nếu bạn sử dụng bất kỳ hàm nào ở trên, hãy giảm, tổng hợp mọi thứ sẽ thông qua

TypeError: nil không thể bị ép buộc vào Integer

Bạn có thể khắc phục điều này bằng cách,

   [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

Phương pháp 6: eval

Đánh giá (các) biểu thức Ruby trong chuỗi.

  [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 cách chúng ta có thể thực hiện tổng hợp mảng

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

2) array.inject('+')

3) array.join('+')


0

Hoặc bạn có thể thử phương pháp này:

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

* Điều này làm việc tốt cho tôi như là một nhà phát triển mới. Bạn có thể điều chỉnh phạm vi số của mình bằng cách thay đổi các giá trị trong []


-1

Bạn cũng có thể làm điều đó một cách dễ dàng

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.