rubyguides

Enumerable#sum

The sum method adds together all elements in a collection. It is part of the Enumerable module, available on arrays, ranges, hashes, and any object that includes Enumerable. Ruby introduced this method in version 2.4 to provide a simpler, more readable alternative to reduce(:+) for common summation tasks.

A useful detail is that sum handles the common cases directly, so the call usually reads like the intent of the code. You can give it a seed value when the total needs to start from something other than zero, or you can pass a block when each element needs to be transformed before it contributes to the final result. That makes the method practical for totals, counts, and small derived values where the code should stay short and obvious.

[1, 2, 3, 4, 5].sum
# => 15

(1..5).sum
# => 15

This quick start shows the two most common shapes: a plain collection total and a range total. The next sections build on that same idea by adding seeds, blocks, and other small adjustments when the default behavior is not quite enough.

Syntax

collection.sum
collection.sum(initial)
collection.sum { |element| block }
collection.sum(initial) { |element| block }

Parameters

ParameterTypeDefaultDescription
initialObject0Starting value added to the sum. For numbers this defaults to zero.
blockBlockNoneOptional block to transform each element before summing.

Basic Usage

Summing Numbers

The most common use case is adding up numbers:

prices = [29.99, 14.99, 9.99, 49.99]
prices.sum
# => 104.96

# Sum of a range
(1..100).sum
# => 5050

Numbers are the easiest case because sum can add them directly without any transformation. Once that base case is clear, it becomes easier to see how the same method handles starting values and transformed elements.

with an initial value

Pass an initial value to start the sum from a different number:

[1, 2, 3].sum(10)
# => 16

# Useful for accumulating across multiple collections
total = [1, 2, 3].sum
total += [4, 5, 6].sum
total += [7, 8, 9].sum
# => 45

The seed value is a good fit when the caller wants a baseline before any items are added. In practice that often means a subtotal, a preloaded string prefix, or a running total that starts from a known amount.

The starting value is useful when the total needs a baseline before any collection items are added. A balance, a prefix string, or a precomputed subtotal all fit the same pattern. It also keeps empty collections predictable, because the seed value is what comes back when there is nothing else to add.

Empty Collections

For an empty collection, sum returns the initial value (zero by default):

[].sum
# => 0

[].sum(100)
# => 100

A seed value also makes behavior predictable for empty collections. Instead of returning nil or raising an error, sum gives back the initial value you provided, which means downstream code does not need a special-case check for emptiness. When no initial value is given, the default of 0 applies, matching the expectation that an empty collection contributes nothing to a total.

Using a Block

Pass a block to transform elements before summing:

# Square each number before summing
(1..4).sum { |i| i * i }
# => 30 (1 + 4 + 9 + 16)

# Sum only even numbers
(1..10).sum { |n| n.even? ? n : 0 }
# => 30 (2 + 4 + 6 + 8 + 10)

# Initial value with block
(1..4).sum(100) { |i| i * i }
# => 130 (100 + 1 + 4 + 9 + 16)

Block form is where sum starts to feel more like a small data-processing tool than a plain addition helper. Each value is transformed first, then the final numbers are combined at the end.

block semantics with an initial value

When using both an initial value and a block, the initial value is added once at the end, not used as the starting accumulator:

[1, 2, 3].sum(10) { |n| n * 2 }
# => 10 + (1*2 + 2*2 + 3*2) = 10 + 12 = 22

The order of operations stays important here: the block handles the collection items, and the initial value stays separate until the final total is produced. That makes the result predictable even when the transformation changes the size of each element.

That ordering matters when the block changes the scale of each item. The seed value stays separate from the transformed elements, which makes the result easier to reason about and avoids accidentally counting the starting value more than once.

With Hashes

scores = { alice: 85, bob: 92, carol: 78 }

# Sum values
scores.sum { |_name, score| score }
# => 255

# Sum with transformation
scores.sum(0) { |name, score| name.to_s.length + score }
# => 266 (length of :alice (5) + 85 + length of :bob (3) + 92 + length of :carol (5) + 78)

Hashes are naturally enumerable over their key-value pairs, so sum fits cleanly into hash-processing pipelines. Inside a hash block, you decide which part of each pair contributes to the total, which means you can sum values, key lengths, or any derived quantity in one pass. This is especially handy when working with counted data, score tables, or any structure where the keys are labels and the values are the numbers you care about.

With Strings

# Concatenate strings
["hello", "world"].sum("")
# => "helloworld"

# With initial string
["a", "b", "c"].sum("prefix-")
# => "prefix-abc"

# With block - take first character of each
["cat", "dog", "bird"].sum("") { |word| word[0].to_s }
# => "cdb"

String concatenation through sum is a niche use case, but it shows how the method delegates to the + operator of whatever type the initial value belongs to. For most string-joining tasks, Array#join is still the idiomatic choice, but sum can be handy when you are already working with an enumerable pipeline.

Difference from inject/reduce

sum is specialized for addition, making it more readable than reduce for simple summation:

# Using reduce
[1, 2, 3].reduce(0, :+)
# => 6

# Using sum (cleaner)
[1, 2, 3].sum
# => 6

Key differences:

Aspectsumreduce
PurposeSpecifically for additionGeneral accumulation
ReadabilityIntuitive for sumsMore flexible but verbose
PerformanceOptimized for numeric sumsGeneral purpose overhead
Initial value default0 for numbersMust be specified

Floating point precision

Like all floating point arithmetic, sum inherits precision limitations:

[0.1, 0.2].sum
# => 0.30000000000000004

# For precise decimal arithmetic, use BigDecimal:
require 'bigdecimal'
[BigDecimal("0.1"), BigDecimal("0.2")].sum
# => 0.3

If exact decimal math matters, use a decimal type instead of a binary float. sum does not change Ruby’s numeric model, so the result can only be as precise as the values you feed into it.

If precision matters, keep the arithmetic in a decimal type instead of a binary float. sum does not change how Ruby represents numeric values, so the method is only as exact as the underlying number objects allow. For financial calculations, use BigDecimal throughout the pipeline before calling sum.

Practical Examples

Calculating Averages

Combine sum with count to calculate averages:

grades = [85, 90, 78, 92, 88]
average = grades.sum / grades.count
# => 86

This example uses integer division, so the result is truncated toward zero. If you need a floating-point average, convert one of the operands with to_f before dividing. The same sum + count pattern also works for weighted averages when combined with a block that multiplies each element by its weight.

Conditional Sum

Sum only elements matching a condition:

transactions = [100, -50, 200, -25, 150]

# Sum only positive transactions (income)
income = transactions.sum { |t| t > 0 ? t : 0 }
# => 450

# Sum only negative transactions (expenses)
expenses = transactions.sum { |t| t < 0 ? t : 0 }
# => -75

By returning zero for unwanted elements, the block effectively filters and sums in a single pass. This is more concise than chaining select and sum, and it avoids allocating an intermediate array. The same technique works for any condition, including range checks, type tests, and more complex predicates.

totaling prices with tax

prices = [19.99, 29.99, 9.99]
tax_rate = 0.08

total_with_tax = prices.sum { |price| price * (1 + tax_rate) }
# => 64.57 (rounded display: $%.2f' % 64.57)

Shopping carts and reports commonly use this pattern, where each element needs a small adjustment before it contributes to the total. The block keeps the rule local, which makes it easy to change tax logic later without rewriting the surrounding code.

counting items by category

inventory = [
  { item: "Apple", quantity: 50, price: 0.50 },
  { item: "Banana", quantity: 100, price: 0.20 },
  { item: "Orange", quantity: 75, price: 0.60 }
]

# Total inventory value
total_value = inventory.sum { |i| i[:quantity] * i[:price] }
# => 95.0

Even though the example is about inventory, the same shape works for any collection of records that needs a derived total. The important part is that the block can do the lookup, the multiplication, and the accumulation all in one pass.

summing nested arrays

matrix = [[1, 2], [3, 4], [5, 6]]

# Sum all elements
matrix.sum { |row| row.sum }
# => 21

# Sum with flatten
matrix.flatten.sum
# => 21

Nested data often needs one extra pass before summing, and this example shows two ways to get there. The explicit nested sum call makes the intent obvious, while flatten.sum is a compact alternative when the shape of the array is already simple.

Performance Notes

sum has built-in optimizations for common cases:

# Ruby optimizes consecutive integer ranges using Gauss's formula
(1..1000000).sum
# => 500000500000 (instant, not a loop)

# For arrays of numbers, sum is faster than reduce(:+)
# because it avoids some Enumerable overhead

Return Value

sum returns the accumulated total:

  • Numbers: returns an Integer or Float depending on inputs
  • Strings: returns a concatenated String
  • Empty collection with default: returns 0 (the numeric zero)
  • Empty collection with initial: returns that initial value unchanged

Edge Cases

# Single element
[5].sum
# => 5

# Single element with initial
[5].sum(10)
# => 15

# All zero values
[0, 0, 0].sum
# => 0

# Mixed positive and negative
[-10, 5, 3, -2].sum
# => -4

See Also