Hash#shift using default values - rubycademy

By Mehdi Farsi

Source: unsplash.com
Mehdi Farsi

The 3 main ways to declare a default value to a Hash are:

  • by using the Hash.new('default value') method
  • by using the Hash#default= method
  • by using the Hash.new { |h, k| h[k] = 'default value' }

Each of these 3 methods produces a different behavior using the Hash#shift method.

Hash#shift

The hash#shift method removes and returns a pair key/value of a hash

Here, we can see that the initial hash contains a pair foo: :bar.

After the call to hash.shift, the hash is empty and the pair [:foo, :bar] is returned and stored in the pair variable.

Then if we call the hash.shift method on an empty hash nil is returned.

Now that we’re more familiar with the Hash#shift method, let’s see how it behaves when the hash defines a default value.

Hash#default=

Here, we define an empty hash. Then we set the 'val' default value for this hash.

Finally, when we call the hash.shift method we notice that our default value is returned as the hash is empty.

Hash.new(default_value)

Here we define an empty hash using the Hash.new with an argument as default value.

When we call the hash.shift method, we notice the exact same behavior as a hash that sets default value using the Hash#default= method.

Hash.new {}

Here we define an empty hash using the Hash.new with a block that defines the default value.

Then we call the hash.shift method that returns the hash default value.

But a second call to hash.shift returns a pair with nil as key and our default value as value.

Weird..

Not at all. Let’s detail what happens here in the following section.

blocks and Hash#shift

Let’s add few lines to our previous example to understand what’s happens step-by-step

Here, we display the content of our hash after a call to hash.shift and we notice that it contains a nil => 'val' pair.

Then we call hash.shift for the second time and logically this pair is returned.

So, why this pair is inserted in our hash after the first call to hash.shift?

The answer is simple: because the block passed to Hash.new is executed each time that you code tries to access the default value of our hash.

Indeed, the call to hash.shift forces our hash to execute the block passed to Hash.new as its return value is used as default value of hash — when hash is empty.

But the subtlety here is that k in the |h, k| block arguments is nil.

So, the block is processed as following:

  • Step 1: hash[nil] = ‘val’
  • Step 2: The hash#[]= method returns the value passed as argument.
  • Step 3: 'val' is returned as this instruction is the latest one executed in our block.

This is why the first call to hash.shift return 'val' and sets the pair { nil => 'val' } in our hash.

So a second call to hash.shift logically returns [nil, 'val'] and not the default value as the hash is not empty at this stage.

Conclusion

When using Hash#shift or any methods that interact with the default value of the hash, be aware if you use a block as default value of the hash.

Indeed, any assignations within this block can generate side effects that can roughly alter the behavior of your app.

Voilà