41

I am having trouble with the syntax for reduce. I have a hash of the following format:

H = {"Key1" => 1, "Key2" => 2}

I would like to use reduce to find the sum of the values in this function.

Something Like

H.reduce(0) {|memo, elem| memo+=elem}

I know this is wrong. I dont understand how I can make elem the value of the hash.

richsoni
  • 4,188
  • 8
  • 34
  • 47

6 Answers6

86

You can make elem contain the value by splitting it up in 2 variables:

H.reduce(0) {|memo, (key, val)| memo + val}
adamors
  • 2,672
  • 5
  • 32
  • 45
steenslag
  • 79,051
  • 16
  • 138
  • 171
42

Use Enumerable#reduce, if you're ok with getting nil if the hash happens to be empty:

H.values.reduce(:+) # => 3
Hash.new.values.reduce(:+) # => nil

To safely get 0 when the hash is empty, use:

H.values.reduce(0) { |sum,x| sum + x } # or...
H.reduce(0) { |sum,(key,val)| sum + val } # ...if you need to inspect the key

Here's a quick benchmark, for kicks. Note that it appears to be slightly faster to reduce just the values rather than values from the key/value pairs:

                               user     system      total        real
H.values.reduce(:+)        4.510000   0.080000   4.590000 (  4.595229)
H.values.reduce(0) {...}   4.660000   0.080000   4.740000 (  4.739708)
H.reduce(0) {...}          5.160000   0.070000   5.230000 (  5.241916)
require 'benchmark'

size = 1_000
hash = Hash[* Array.new(size*2) { rand } ]

N=10_000
Benchmark.bm(24) do |x|
  x.report('H.values.reduce(:+)')      { N.times { hash.dup.values.reduce(:+) } }
  x.report('H.values.reduce(0) {...}') { N.times { hash.dup.values.reduce(0) { |sum,x| sum + x } } }
  x.report('H.reduce(0) {...}')        { N.times { hash.dup.reduce(0) { |sum,(_,v)| sum + v } } }
end
maerics
  • 151,642
  • 46
  • 269
  • 291
6

Try this:

H.reduce(0) { |memo, elem| memo += elem[1] }

or

H.reduce(0) { |memo, (key, value)| memo += value }
yasu
  • 1,374
  • 8
  • 16
2

I know I'm excavating this one, but if you happen to use Rails, the .sum method can help:

H = {"Key1" => 1, "Key2" => 2}
=> {"Key1"=>1, "Key2"=>2}
> H.values.sum
=> 3

Advantage is that it returns 0 on empty hashes:

> {}.values.sum
=> 0
> {}.values.reduce(:+)
=> nil

I noticed it was Rails-specific only after typing this answer. I know the OP didn't add the Rails tag, but I figured it might be useful for people stopping by.

Note that as of Ruby 2.4.0, .sum is now available.

astorije
  • 2,666
  • 2
  • 27
  • 39
  • 1
    `.sum` is now implemented in Ruby 2.4 [Enumerable#sum](https://ruby-doc.org/core-2.4.0/Enumerable.html#method-i-sum). – user3162553 May 16 '17 at 19:25
1
h = {"Key1" => 1, "Key2" => 2}

h.values.inject(0){|f,v| f += v.to_i }
# => 3

or

h.values.inject(:+)
# => 3
0

In case of a complex hash it might be easier to map it first to an array of values, then reduce:

values = H.map do |k, v|
    # some complex logic here
end
values.reduce(:+)

Or values.reduce(0, :+) if the array might be empty.

x-yuri
  • 16,722
  • 15
  • 114
  • 161