RuboCop suggests:
Use
Array.new
with a block instead of.times.map.
In the docs for the cop:
This cop checks for .times.map calls. In most cases such calls can be replaced with an explicit array creation.
Examples:
# bad 9.times.map do |i| i.to_s end # good Array.new(9) do |i| i.to_s end
I know it can be replaced, but I feel 9.times.map
is closer to English grammar, and it's easier to understand what the code does.
Why should it be replaced?
The latter is more performant; here is an explanation: Pull request where this cop was added
It checks for calls like this:
9.times.map { |i| f(i) } 9.times.collect(&foo)
and suggests using this instead:
Array.new(9) { |i| f(i) } Array.new(9, &foo)
The new code has approximately the same size, but uses fewer method calls, consumes less memory, works a tiny bit faster and in my opinion is more readable.
I've seen many occurrences of times.{map,collect} in different well-known projects: Rails, GitLab, Rubocop and several closed-source apps.
Benchmarks:
Benchmark.ips do |x| x.report('times.map') { 5.times.map{} } x.report('Array.new') { Array.new(5){} } x.compare! end __END__ Calculating ------------------------------------- times.map 21.188k i/100ms Array.new 30.449k i/100ms ------------------------------------------------- times.map 311.613k (± 3.5%) i/s - 1.568M Array.new 590.374k (± 1.2%) i/s - 2.954M Comparison: Array.new: 590373.6 i/s times.map: 311612.8 i/s - 1.89x slower
I'm not sure now that Lint is the correct namespace for the cop. Let me know if I should move it to Performance.
Also I didn't implement autocorrection because it can potentially break existing code, e.g. if someone has Fixnum#times method redefined to do something fancy. Applying autocorrection would break their code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With