Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new cop Performance/UnneededSort #5753

Merged
merged 2 commits into from
Apr 16, 2018

Conversation

parkerfinch
Copy link
Contributor

Usage of sort.last and sort.first can be replaced with max and
min, respectively. Similarly, sort_by(&:something).first and
sort_by(&:something).last can be replaced with the more efficient min_by(&:something)andmax_by(&:something)`.

This cop identifies and autocorrects these unneeded sorts.

This cop does not register offenses if the first or last call has an
argument, because sort.last(n) is not equivalent to max(n). This
cop does not register offenses for the bang sorts (sort! and
sort_by!) because their side-effects may be used.


Before submitting the PR make sure the following are checked:

  • Wrote good commit messages.
  • Commit message starts with [Fix #issue-number] (if the related issue exists).
  • Feature branch is up-to-date with master (if not - rebase it).
  • Squashed related commits together.
  • Added tests.
  • Added an entry to the Changelog if the new code introduces user-observable changes. See changelog entry format.
  • The PR relates to only one subject with a clear title
    and description in grammatically correct, complete sentences.
  • Run rake default or rake parallel. It executes all tests and RuboCop for itself, and generates the documentation.

@Drenmi
Copy link
Collaborator

Drenmi commented Apr 8, 2018

Code looks good to me. One comment on the cop name. There's already a small eco system of names for different cases of "unneeded", "redundant", etc. Would prefer using one of those instead of introducing additional "superfluous". 🙂

@parkerfinch
Copy link
Contributor Author

Thanks @Drenmi , makes sense, I'll switch it over to UnneededSort!

@parkerfinch parkerfinch changed the title Add a new cop Performance/SuperfluousSort Add a new cop Performance/UnneededSort Apr 8, 2018
@parkerfinch parkerfinch force-pushed the superfluous-sort-cop branch 3 times, most recently from f2b1fc9 to 8c0ad4b Compare April 8, 2018 12:00
@parkerfinch
Copy link
Contributor Author

I changed the cop name, although the branch still references superfluous. Should I open a new PR with a renamed branch?

@koic
Copy link
Member

koic commented Apr 8, 2018

No, the branch name should remain old. It is not important when investigating history. Also, it is more convenient to see conversations with the same PR in this case. Thanks for your concern.

@bdewater
Copy link
Contributor

bdewater commented Apr 8, 2018

For #5643 I discovered a few different ways of accessing the first element which you might also want to cover in this cop.

Like suggested in that PR, please consider adding benchmarks to https://github.com/JuanitoFatas/fast-ruby as well for the community to learn from 😄

include RangeHelp

MSG = 'Use `%<suggestion>s` instead of '\
'`%<sorter>s...%<accessor>s`.'.freeze
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of using the actual code snippet here instead of ...?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds reasonable, but I'm not completely sold! I'm a little worried about the case when the actual code snippet is nontrivial:

Use `min_by` instead of `sort_by...first`

is clear about what both the offense and preferred approach are, while

Use `min_by do |x|
  a = x.first
  b = x.last
  [a.length, b.length].max
end` instead of `sort_by do |x|
  a = x.first
  b = x.last
  [a.length, b.length].max
end.first`

is more difficult to interpret. Any thoughts there?

If you do still want to do the the full code snippet approach, what's the best way to generate the preferred code outside of the autocorrect method? Is there a way to access a similar Corrector that doesn't actually affect the source code?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I wasn't really thinking about multi-line examples. The shorthand using sort_by...first will be fine.

subject(:cop) { described_class.new }

it 'registers an offense when first is called with sort' do
inspect_source('[1, 2, 3].sort.first')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the expect_offense and expect_no_offenses helpers.

expect_offense(<<-RUBY.strip_indent)
  [1, 2, 3].sort.first
            ^^^^^^^^^^ Use `min` instead of `sort...first`.
RUBY

@parkerfinch
Copy link
Contributor Author

Added more ways of accessing the first/last element suggested by @bdewater and used the spec helpers suggested by @rrosenblum!

@parkerfinch
Copy link
Contributor Author

Squashed commits and edited the commit message to reflect the new scope of this cop.

Let me know if there's anything else I can do to help this across the finish line!

CHANGELOG.md Outdated
@@ -20,6 +20,7 @@

* [#5597](https://github.com/bbatsov/rubocop/pull/5597): Add new `Rails/HttpStatus` cop. ([@anthony-robin][])
* [#5643](https://github.com/bbatsov/rubocop/pull/5643): Add new `Style/UnpackFirst` cop. ([@bdewater][])
* Add new `Performance/UnneededSort` cop. ([@parkerfinch][])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can add the PR # and link :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah nice will do, thanks!

CHANGELOG.md Outdated
@@ -20,6 +20,7 @@

* [#5597](https://github.com/bbatsov/rubocop/pull/5597): Add new `Rails/HttpStatus` cop. ([@anthony-robin][])
* [#5643](https://github.com/bbatsov/rubocop/pull/5643): Add new `Style/UnpackFirst` cop. ([@bdewater][])
* [#5753](https://github.com/bbatsov/rubocop/pull/5753): Add new `Performance/UnneededSort` cop. ([@parkerfinch][])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't expand the preview on GH until now - sorry to be a bother - noticed this is under the '0.54.0' header instead of 'master' probably due to the rebase.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whew, good catch! Fixed.

A `sort` followed by retrieving only the first or last element of the
resulting array can be replaced with a `min` or `max` call. For
example, `sort.first` can be replaced with `min` while `sort[-1]` can
be replaced with `max`. Similarly, `sort_by(&:something).first` and
`sort_by(&:something).last` can be replaced with the more efficient
`min_by(&:something)` and `max_by(&:something)`.

This cop identifies and autocorrects these unneeded sorts. This cop
does *not* register offenses if the `first` or `last` call has an
argument, because `sort.last(n)` is not equivalent to `max(n)`. This
cop does *not* register offenses for the bang sorts (`sort!` and
`sort_by!`) because their side-effects may be used.
@parkerfinch
Copy link
Contributor Author

Rebased onto master to resolve CHANGELOG.md issues.

@bbatsov bbatsov merged commit ec3bdbf into rubocop:master Apr 16, 2018
@bbatsov
Copy link
Collaborator

bbatsov commented Apr 16, 2018

Nice job!

Just one small feedback - I think that a lot of the general explanations you've mentioned in the PR description would be useful addition to the cop's description. It also seems that you haven't added examples there for for everything the cop actually covers.

@parkerfinch parkerfinch deleted the superfluous-sort-cop branch April 16, 2018 11:50
@parkerfinch
Copy link
Contributor Author

Thanks @bbatsov! Per your feedback, opened #5787 with a more general overview in the cop description and a more complete set of examples.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants