Hash#fetch and default value

You’ve been using Hash#fetch and are happy with it. You also know that you can specify default return value in case the key you are looking for is not present in hash.

1
2
3
4
5
{}.fetch(:test) # => KeyError: key not found: :test

{}.fetch(:test, "test") # => "test"

{}.fetch(:test) { "test" } # => "test"

Lines 3 and 5 return the same result so what’s the difference you may ask? Lets use a complex method for a default value which takes a couple of seconds to process. We will simulate slowness with sleep. Here is a code with some benchmarks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
require "benchmark"

def expensive_method
  # this will simulate slowness
  sleep 3
  1 + 1
end

Benchmark.bm do |bm|
  bm.report("argument - key not present") do
    {}.fetch(:test, expensive_method)
  end

  bm.report("argument - key present") do
    { :test => "test" }.fetch(:test, expensive_method)
  end

  bm.report("block - key not present") do
    {}.fetch(:test) { expensive_method }
  end

  bm.report("block - key present") do
    { :test => "test" }.fetch(:test) { expensive_method }
  end
end

#                                  user     system      total       real
# argument - key not present    0.000000   0.000000   0.000000 (  3.001072)
# argument - key present        0.000000   0.000000   0.000000 (  3.001089)
# block - key not present       0.000000   0.000000   0.000000 (  3.001100)
# block - key present           0.000000   0.000000   0.000000 (  0.000013)

See how expensive_method gets executed in argument – key present benchmark blocking everything for 3+ seconds even know :test key is present. We want expensive_method to be run only when :test key is not present and that is why you should use block when specifying default value. Block gets executed only when the key is not present as opposed to argument version which always executes expensive_method no matter if the key is present or not. While this probably won’t bite most of you, I still think it is good to know about this “gotcha”.

I learned about this while watching one of the RubyTapas episodes. RubyTapas are short screencasts related to Ruby by Avdi Grimm. If you’re not subscribed to this resource I highly encourage you to do so – knowledge bombs Avdi keeps dropping are really invaluable.


I'm currently looking for Ruby on Rails and/or Ember.js consulting gigs. Have anything in mind? Please don't hesitate to click here and get in touch with me. I don't bite ;)

Comments