rails/rails — pull request #16149 by sferik (2014-07-12 15:17:42+00:00)
Title: Refactor Hash#transform_values and Hash#transform_values!

This patch makes two small changes to the new Active Support core extensions `Hash#transform_values` and `Hash#transform_values!`:

1. These methods now return an `Enumerator` if no block is given. This makes them more consistent with other `Enumerable` methods and allows for method chaining, for example:

```ruby
{a: 1, b: 2, c: 3}.transform_values.with_index{|x, i| [x * x, i]}
#=> {:a=>[1, 0], :b=>[4, 1], :c=>[9, 2]}
```

Before this patch, such chaining would result in a `LocalJumpError: no block given`.

2. Since the `&block` parameter is never referenced, I’ve removed it from `Hash#transform_values`. This prevents a garbage Proc object from being constructed, resulting in measurably faster performance. Here is the benchmark I ran:

```ruby
require 'benchmark/ips'

class Hash
def slow(&block)
return enum_for(:slow) unless block_given?
result = self.class.new
each do |key, value|
result[key] = yield(value)
end
result
end

def fast
return enum_for(:fast) unless block_given?
result = self.class.new
each do |key, value|
result[key] = yield(value)
end
result
end
end

HASH = {a: 1, b: 2, c: 3}

Benchmark.ips do |x|
x.report("slow") { HASH.slow { |x| x * x } }
x.report("fast") { HASH.fast { |x| x * x } }
end
```

```
slow 388660.2 (±10.6%) i/s - 1931275 in 5.030443s
fast 621865.9 (±11.5%) i/s - 3085425 in 5.029155s
```