What is the difference between 'keys.each' and 'each_key'?

Viewed 2163

When iterating through a hash like below:

hash.keys.each do |key|
  process_key(key)
end

Rubocop proclaims I should use:

each_key

instead of:

keys.each

What is the "key" difference between keys.each and each_key?

2 Answers

Rubocop wants you to follow this based on your evaluated code due to performance. Using large sets of data is where this becomes noticeable. Here's the doc on it: https://github.com/bbatsov/rubocop/blob/master/manual/cops_performance.md#performancehasheachmethods

I also found a benchmark someone wrote up to test this: https://gist.github.com/jodosha/8ca2bee6137be94e9dcb

I modified it a bit and ran it on on of my systems:

Warming up --------------------------------------
         string each   128.742k i/100ms
         string keys   114.523k i/100ms
     string each_key   134.279k i/100ms
         symbol each   128.838k i/100ms
         symbol keys   109.398k i/100ms
     symbol each_key   132.021k i/100ms
Calculating -------------------------------------
         string each      2.053M (± 4.0%) i/s -     10.299M in   5.026890s
         string keys      1.864M (± 1.4%) i/s -      9.391M in   5.039759s
     string each_key      2.224M (± 5.5%) i/s -     11.145M in   5.032201s
         symbol each      2.082M (± 1.0%) i/s -     10.436M in   5.013145s
         symbol keys      1.815M (± 2.1%) i/s -      9.080M in   5.004690s
     symbol each_key      2.240M (± 1.9%) i/s -     11.222M in   5.012184s

Comparison:
     symbol each_key:  2239720.0 i/s
     string each_key:  2224205.1 i/s - same-ish: difference falls within error
         symbol each:  2081895.2 i/s - 1.08x  slower
         string each:  2052884.9 i/s - 1.09x  slower
         string keys:  1863740.5 i/s - 1.20x  slower
         symbol keys:  1815131.1 i/s - 1.23x  slower

Chaining methods is going to be slower than using the built-in method (in this case) which accomplishes the task with a single special enumerator. The language creators put it there for a reason, also its idiomatic.

Rubocop is wrong. Which one you should use depends on what you want as the return value.

  1. If you want an array of the keys back, then you should use keys.each. key creates a new array of keys, and each returns that array of keys after performing the block on each key.
  2. If you do not need to have an array of keys (and want the original hash back), then you should use each_key since that does not create an array that would not be used, and would be more efficient.
Related