123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Performance::MapCompact

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, TargetRubyVersion, AutoCorrector, Base
Instance Chain:
self, RangeHelp, Base
Inherits: Base
  • Object
Defined in: lib/rubocop/cop/performance/map_compact.rb

Overview

In Ruby 2.7, Enumerable#filter_map has been added.

This cop identifies places where map { …​ }.compact can be replaced by filter_map.

[true, false, nil].compact              #=> [true, false]
[true, false, nil].filter_map(&:itself) #=> [true]

Examples:

# bad
ary.map(&:foo).compact
ary.collect(&:foo).compact

# good
ary.filter_map(&:foo)
ary.map(&:foo).compact!
ary.compact.map(&:foo)

Cop Safety Information:

  • This cop’s autocorrection is unsafe because map { …​ }.compact might yield different results than filter_map. As illustrated in the example, filter_map also filters out falsy values, while compact only gets rid of nil.

Constant Summary

Instance Method Summary

Instance Method Details

#compact_method_with_final_newline_range(compact_method_range) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/map_compact.rb', line 95

def compact_method_with_final_newline_range(compact_method_range)
  range_by_whole_lines(compact_method_range, include_final_newline: true)
end

#invoke_method_after_map_compact_on_same_line?(compact_node, chained_method) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/map_compact.rb', line 91

def invoke_method_after_map_compact_on_same_line?(compact_node, chained_method)
  compact_node.loc.selector.line == chained_method.loc.last_line
end

#map_method_and_compact_method_on_same_line?(map_node, compact_node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/map_compact.rb', line 87

def map_method_and_compact_method_on_same_line?(map_node, compact_node)
  compact_node.loc.selector.line == map_node.loc.selector.line
end

#on_csend(node)

Alias for #on_send.

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/map_compact.rb', line 65

alias on_csend on_send

#on_send(node) Also known as: #on_csend

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/map_compact.rb', line 54

def on_send(node)
  return unless (map_node = map_compact(node))

  compact_loc = node.loc
  range = range_between(map_node.loc.selector.begin_pos, compact_loc.selector.end_pos)

  add_offense(range) do |corrector|
    corrector.replace(map_node.loc.selector, 'filter_map')
    remove_compact_method(corrector, map_node, node, node.parent)
  end
end

#remove_compact_method(corrector, map_node, compact_node, chained_method) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/map_compact.rb', line 69

def remove_compact_method(corrector, map_node, compact_node, chained_method)
  compact_method_range = compact_node.loc.selector

  if compact_node.multiline? && chained_method&.loc.respond_to?(:selector) && use_dot?(chained_method) &&
     !map_method_and_compact_method_on_same_line?(map_node, compact_node) &&
     !invoke_method_after_map_compact_on_same_line?(compact_node, chained_method)
    compact_method_range = compact_method_with_final_newline_range(compact_method_range)
  else
    corrector.remove(compact_node.loc.dot)
  end

  corrector.remove(compact_method_range)
end

#use_dot?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/map_compact.rb', line 83

def use_dot?(node)
  node.respond_to?(:dot?) && node.dot?
end