Як підсумовувати масив чисел у Ruby?


563

У мене є масив цілих чисел.

Наприклад:

array = [123,321,12389]

Чи є якийсь приємний спосіб отримати суму їх?

Я знаю це

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

працювали б.


19
Зверніть увагу, що Ruby 2.4+ маєarray.sum
dawg

У Ruby 2.6 його немає. Рубі дає, Рубі знімає, здається.
Лорі

1
@ Лорі хм? посилання
steenslag

Вибачте. У той час я помилково вважав, що використовую 2.6 через пробудження rbenv з мого боку.
Лорі

Відповіді:


612

Спробуйте це:

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

Див. Перелічену документацію Рубі

(зверніть увагу: 0базовий регістр потрібен, щоб 0він повертався на порожній масив замість nil)


317
jorney's array.inject(:+)є більш ефективним.
Пітер

3
array.inject(:+)Схоже, це спричинить проблеми в Ruby 1.8.6 Виключення "LocalJumpError: не вказано блоку" може спливати.
Каміль Шот

34
У рейках array.sumможе бути вказана сума значень масиву.
Каміль Шот

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 (: +)?
Пабло Кантеро

7
Ви можете скористатися аналогічним трюком методом карти: array.map (&: ціна) .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

Кен Іверсон представив оператора / називав "оператором скорочення" в мові програмування APL. Джерело: Іверсон, Кеннет. 1962. Мова програмування. Вілі. Інше джерело: "Позначення як інструмент думки", 1979 р. Лекція премії ACM Тьюрінга, Кеннет Е. Іверсон, dl.acm.org/ft_gateway.cfm?id=1283935&type=pdf
Фернандо

112

Як варіант (лише для порівняння), якщо у вас встановлено Rails (насправді лише ActiveSupport):

require 'activesupport'
array.sum

12
Новіші версії активної підтримки фактично не завантажують усі розширення за замовчуванням. Ви хочете , щоб вимагати або тільки модуль суми: require 'active_support/core_ext/enumerable.rb'або вимагати все активної підтримки: require 'active_support/all'. Більше про це тут: API Документи
dcashman

2
Не важливо , що activesupportце масивне залежність перетягнути в проект , щоб перейти від array.inject(:+)до array.sum.
meagar

1
Нітпік до інакшого хорошого коментаря: він повинен бути require 'active_support/core_ext/enumerable'без .rbсуфікса, оскільки це додається неявно.
Пер Лундберг

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
Не робіть цього
користувач3467349

@ user3467349 чому?
YoTengoUnLCD

15
Базові класи мавп-спостережень - це не приємно.
користувач3467349

1
Сенс, який він зазначає, полягає в тому, що вам не потрібно робити Monkey Patch for Ruby> = 2.4, і те, що виправлення мавп є небезпечним, і що тепер ви можете створювати підрахунки на самому собі, але також є спосіб підтримувати функціональність.
Пітер Х. Болінг

Захищений, тому що ваша реалізація повертає нуль на порожні масиви.
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, :+). Дивіться інші відповіді.
Річард Джонс

4
Певним чином, так. Однак використання mapцього injectвимагає провести цикл через масив двічі: один раз для створення нового масиву, інший для підсумовування членів. Цей метод трохи більш багатослівний, але також більш ефективний.
HashFail

По- видимому, не більше ефективний, см gist.github.com/cameron-martin/b907ec43a9d8b9303bdc - кредит на коментарі в цій відповіді: stackoverflow.com/a/1538949/1028679
rmcsharry

19

Ruby 2.4+ / Рейки - 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 ти прав! Радий побачити цю корисну функцію.
збирати

18

Рубін 1.8.7 спосіб такий:

array.inject(0, &:+) 

Якщо ви прочитаєте мій коментар 2011 року, і він все ще актуальний, оскільки ви використовуєте 1.8.6, будь ласка, оновіть!
Ендрю Грімм



5

Ruby 2.4.0 випущений, і він має метод Enumerable # sum . Так ви можете зробити

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

для масиву з нульовими значеннями ми можемо зробити компактний і потім ввести суму ex-

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

2
array.reduce(:+)

Працює і для діапазонів ... отже,

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

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: Коли масив містить нульові та порожні значення, за замовчуванням, якщо ви використовуєте будь-яку з наведених вище функцій, зменшуйте, сумуйте, вводите все через

TypeError: nil не може бути примушений до 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

Оцінює вирази (-и) Ruby у рядку.

  [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

Або ви можете спробувати цей метод:

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

* Це добре працювало для мене як нового розробника. Ви можете скорегувати діапазон чисел, змінивши значення в межах []


-1

Ви також можете це зробити простим способом

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

-8

Ви можете використовувати .map і .sum як:

array.map { |e| e }.sum

3
Який сенс зробити карту, що повертає той самий елемент? це точно так само, якarray.sum
Арнольд Роа

Більше того, array.sum не існує в рубіні. Дивіться відповідь Майка Вудхауза
Ulysse BN

Це робиться зараз у Ruby 2.4.0
встановлення
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.