Class: RuboCop::Cop::Style::TallyMethod

Inherits:
Base
  • Object
show all
Extended by:
AutoCorrector, TargetRubyVersion
Includes:
RangeHelp
Defined in:
lib/rubocop/cop/style/tally_method.rb

Overview

Checks for manual counting patterns that can be replaced by ‘Enumerable#tally`.

The cop detects the following patterns:

  • ‘each_with_object(Hash.new(0)) { |item, counts| counts += 1 }`

  • ‘group_by(&:itself).transform_values(&:count)`

  • ‘group_by { |x| x }.transform_values(&:size)`

  • ‘group_by { |x| x }.transform_values { |v| v.length }`

Examples:

# bad
array.each_with_object(Hash.new(0)) { |item, counts| counts[item] += 1 }

# bad
array.group_by(&:itself).transform_values(&:count)

# bad
array.group_by { |item| item }.transform_values(&:size)

# bad
array.group_by { |item| item }.transform_values { |v| v.length }

# good
array.tally

Constant Summary collapse

MSG_EACH_WITH_OBJECT =
'Use `tally` instead of `each_with_object`.'
MSG_GROUP_BY =
'Use `tally` instead of `group_by` and `transform_values`.'
RESTRICT_ON_SEND =
i[each_with_object transform_values].freeze
COUNTING_METHODS =
i[count size length].to_set.freeze

Constants included from RangeHelp

RangeHelp::BYTE_ORDER_MARK, RangeHelp::NOT_GIVEN

Instance Attribute Summary

Attributes inherited from Base

#config, #processed_source

Instance Method Summary collapse

Methods included from AutoCorrector

support_autocorrect?

Methods included from TargetRubyVersion

maximum_target_ruby_version, minimum_target_ruby_version, required_maximum_ruby_version, required_minimum_ruby_version, support_target_ruby_version?

Methods inherited from Base

#active_support_extensions_enabled?, #add_global_offense, #add_offense, #always_autocorrect?, autocorrect_incompatible_with, badge, #begin_investigation, #callbacks_needed, callbacks_needed, #config_to_allow_offenses, #config_to_allow_offenses=, #contextual_autocorrect?, #cop_config, #cop_name, cop_name, department, documentation_url, exclude_from_registry, #excluded_file?, #external_dependency_checksum, inherited, #initialize, #inspect, joining_forces, lint?, match?, #message, #offenses, #on_investigation_end, #on_new_investigation, #on_other_file, #parse, #parser_engine, #ready, #relevant_file?, requires_gem, #string_literals_frozen_by_default?, support_autocorrect?, support_multiple_source?, #target_gem_version, #target_rails_version, #target_ruby_version

Methods included from ExcludeLimit

#exclude_limit

Methods included from AutocorrectLogic

#autocorrect?, #autocorrect_enabled?, #autocorrect_requested?, #autocorrect_with_disable_uncorrectable?, #correctable?, #disable_uncorrectable?, #safe_autocorrect?

Methods included from IgnoredNode

#ignore_node, #ignored_node?, #part_of_ignored_node?

Methods included from Util

silence_warnings

Constructor Details

This class inherits a constructor from RuboCop::Cop::Base

Instance Method Details

#on_send(node) ⇒ Object Also known as: on_csend



129
130
131
132
133
134
135
# File 'lib/rubocop/cop/style/tally_method.rb', line 129

def on_send(node)
  if node.method?(:each_with_object)
    check_each_with_object(node)
  elsif node.method?(:transform_values)
    check_transform_values(node)
  end
end

#tally_each_with_object?(node) ⇒ Object

Pattern 1: collection.each_with_object(Hash.new(0)) { |elem, hash| hash += 1 }



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/rubocop/cop/style/tally_method.rb', line 50

def_node_matcher :tally_each_with_object?, "{\n  (block\n    (call _ :each_with_object\n      (send (const {nil? cbase} :Hash) :new (int 0)))\n    (args (arg _elem) (arg _hash))\n    (op_asgn\n      (send (lvar _hash) :[] (lvar _elem)) :+ (int 1)))\n  (numblock\n    (call _ :each_with_object\n      (send (const {nil? cbase} :Hash) :new (int 0)))\n    2\n    (op_asgn\n      (send (lvar :_2) :[] (lvar :_1)) :+ (int 1)))\n}\n"

#tally_group_by_identity_block?(node) ⇒ Object

Pattern 3: collection.group_by { |x| x }.transform_values(&:count/size/length)



78
79
80
81
82
83
84
85
86
87
# File 'lib/rubocop/cop/style/tally_method.rb', line 78

def_node_matcher :tally_group_by_identity_block?, "(call\n  {\n    (block (call _ :group_by) (args (arg _x)) (lvar _x))\n    (numblock (call _ :group_by) 1 (lvar :_1))\n    (itblock (call _ :group_by) :it (lvar :it))\n  }\n  :transform_values\n  (block_pass (sym %COUNTING_METHODS)))\n"

#tally_group_by_symbol?(node) ⇒ Object

Pattern 2: collection.group_by(&:itself).transform_values(&:count/size/length)



69
70
71
72
73
74
# File 'lib/rubocop/cop/style/tally_method.rb', line 69

def_node_matcher :tally_group_by_symbol?, "(call\n  (call _ :group_by (block_pass (sym :itself)))\n  :transform_values\n  (block_pass (sym %COUNTING_METHODS)))\n"

#tally_group_by_transform_block?(node) ⇒ Object

Pattern 4: collection.group_by(&:itself).transform_values { |v| v.count/size/length }

collection.group_by { |x| x }.transform_values { |v| v.count/size/length }


92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/rubocop/cop/style/tally_method.rb', line 92

def_node_matcher :tally_group_by_transform_block?, "{\n  (block\n    (call\n      {\n        (call _ :group_by (block_pass (sym :itself)))\n        (block (call _ :group_by) (args (arg _x)) (lvar _x))\n        (numblock (call _ :group_by) 1 (lvar :_1))\n        (itblock (call _ :group_by) :it (lvar :it))\n      }\n      :transform_values)\n    (args (arg _v))\n    (send (lvar _v) %COUNTING_METHODS))\n  (numblock\n    (call\n      {\n        (call _ :group_by (block_pass (sym :itself)))\n        (block (call _ :group_by) (args (arg _x)) (lvar _x))\n        (numblock (call _ :group_by) 1 (lvar :_1))\n        (itblock (call _ :group_by) :it (lvar :it))\n      }\n      :transform_values)\n    1\n    (send (lvar :_1) %COUNTING_METHODS))\n  (itblock\n    (call\n      {\n        (call _ :group_by (block_pass (sym :itself)))\n        (block (call _ :group_by) (args (arg _x)) (lvar _x))\n        (numblock (call _ :group_by) 1 (lvar :_1))\n        (itblock (call _ :group_by) :it (lvar :it))\n      }\n      :transform_values)\n    :it\n    (send (lvar :it) %COUNTING_METHODS))\n}\n"