3

Why does [].sum gives an undefined method error?

[5, 15, 10].sum 
# => NoMethodError: undefined method `sum' for [5, 15, 10]:Array 

Doing ri Array#sum returns:

Array#sum

(from gem activesupport-4.2.6) Implementation from Enumerable
------------------------------------------------------------------------------
sum(identity = 0, &block)

------------------------------------------------------------------------------

Calculates a sum from the elements.

payments.sum { |p| p.price * p.tax_rate } payments.sum(&:price)

The latter is a shortcut for:

payments.inject(0) { |sum, p| sum + p.price }

It can also calculate the sum without the use of a block.

[5, 15, 10].sum # => 30                         ## <-- What?! >:(  
['foo', 'bar'].sum # => "foobar"
[[1, 2], [3, 1, 5]].sum => [1, 2, 3, 1, 5]

The default sum of an empty list is zero. You can override this default:

[].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)

What's going on? What am I failing to understand? Or is my installation broken?

sawa
  • 165,429
  • 45
  • 277
  • 381
Jon Carter
  • 2,836
  • 1
  • 20
  • 26
  • 2
    `sum` isn't predefined for either [`Array`s](http://ruby-doc.org/core-2.3.0/Array.html) or [`Enumerable`s](http://ruby-doc.org/core-2.3.0/Enumerable.html). It may be added by a library/framework [such as ActiveSupport](http://api.rubyonrails.org/classes/Enumerable.html#method-i-sum). Otherwise, you can [use `inject` to calculate a sum](https://stackoverflow.com/questions/1538789/how-to-sum-array-of-numbers-in-ruby). – Jonathan Lonowski Jun 05 '16 at 23:23
  • I see. I misread the docs (or just didn't read them carefully enough.) For what it's worth, I'm aware of using inject() like that, but thanks for the suggestion. Map/reduce games are something I particularly like. :) – Jon Carter Jun 05 '16 at 23:37
  • Many things from Rails are making their way over into core Ruby over time, so this line is pretty blurry. Still, watch out for the "ActiveSuport" hint in the docs. – tadman Jun 06 '16 at 04:52

2 Answers2

6

It mentions (from gem activesupport-4.2.6) Implementation from Enumerable.

require 'active_support'
require 'active_support/core_ext'

2.2.2 > [5, 15, 10].sum
=> 30
Nabeel
  • 2,272
  • 1
  • 11
  • 14
  • Ah. So it does. I guess I didn't understand the significance of that bit. – Jon Carter Jun 05 '16 at 23:26
  • So the problem was that I didn't understand the docs. Thanks again. But I'd like to understand this better. It seems to me that sum() should be there as natively as max() is. What's the rationalization? More technically, how is this *implemented*? Are there different Enumerable modules? Or.. it's a guess, but I suppose that active_support adds that method (and perhaps others) to Enumerable. Maybe I can look, if I can find the source. Can anyone suggest some further reading? Is there a programmatic way to examine these things? Like, "Show me the inheritance chain of this method." – Jon Carter Jun 05 '16 at 23:33
  • 1
    `[].method(:sum).source_location` will point to the file used – floum Jun 05 '16 at 23:37
  • Nice! That's going to be useful. :) – Jon Carter Jun 05 '16 at 23:41
  • 1
    The rationale behind it I'm not exactly sure, you could always write ``[5, 15, 10].reduce(:+)`` instead. It might be a good idea to look at the source code for [Active Support Enumerable](https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/enumerable.rb). Active Support adds a lot of useful extensions in general to Ruby, however beware that some things might seem like complete black magic such as ``Time.now + 5.send('days')`` (and you have to dig through a lot of source to find out how it's done). – Nabeel Jun 05 '16 at 23:46
  • [2.4](https://bugs.ruby-lang.org/issues/12217) will have `Array#sum` and `Enumerable#sum`. – cremno Jun 06 '16 at 00:31
  • In my opinion, `#sum` on enumerable doesn't make too much sense because it only works for numerics, and `#reduce(:+)` is not too much longer to write. – Amadan Jun 06 '16 at 00:43
  • @NabeelAmjad: Why `5.send(:days)` when you can `5.days`? – Amadan Jun 06 '16 at 00:44
  • @Amadan sorry, bad example. In my scenario it was for dynamic calling where I would not know if it's days, months, weeks, etc. – Nabeel Jun 06 '16 at 01:15
  • @Amadan, you're right that it's not that much longer, but the whole thing that got me thinking about this was trying to decide whether to use something like .reduce(:+), or sum(), and I found the latter preferable because it reads more clearly -- it seems a lot more obvious what's going on, especially to someone who doesn't recognize reduce(). But I suppose it *is* a judgement call, and context could be relevant. – Jon Carter Jun 06 '16 at 02:53
2

Its already stated above in most answers that sum is not an instance method of array.

You can see all methods available on an object using object.methods. example [1,2,3].methods. Also you can refer to http://apidock.com/ruby/Array

[1,2,3].inject(0) {|sum,x| sum + x }
Garima Singh
  • 233
  • 1
  • 7