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

[Fix #178] Add new Performance/SelectMap cop #211

Merged
merged 1 commit into from
Apr 18, 2021

Conversation

koic
Copy link
Member

@koic koic commented Feb 9, 2021

Fixes #178 and #193.

This PR adds new Performance/SelectMap cop.

In Ruby 2.7, Enumerable#filter_map has been added.

This cop identifies places where select.map can be replaced by filter_map. And it allows map { ... }.compact that is not compatible with filter_map.

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

The following is good and bad cases.

# bad
ary.select(&:foo).map(&:bar)
ary.filter(&:foo).map(&:bar)

# good
ary.filter_map { |o| o.bar if o.foo }

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 bundle exec rake default. It executes all tests and RuboCop for itself, and generates the documentation.

@koic koic force-pushed the add_new_performance_filter_map_cop branch from bd31073 to 11d3c61 Compare February 9, 2021 11:21
@marcandre
Copy link
Contributor

I'm not sure we should enable this cop by default; in many cases (including the examples with foo and bar above) the resulting code is less clear. I didn't check, but I doubt the performance gain is particularly big (especially in the example for &foo & &bar)

@koic
Copy link
Member Author

koic commented Feb 10, 2021

Yeah, I have no strong objection to disabling this cop by default. On the other hand, Performance department's cop may prioritize performance over code readability (e.g. A.ancestors.include?(B) is easier to read than A <= B, but A <= B is a good case for Performance/AncestorsInclude cop). In any case, I think this cop can start with disabled by default.

@koic koic force-pushed the add_new_performance_filter_map_cop branch 3 times, most recently from 2751929 to 28b6369 Compare February 17, 2021 03:35
@bbatsov
Copy link
Contributor

bbatsov commented Mar 15, 2021

I agree that generally it's fine to have the performance cop optimize for speed over clarity. After all that's what they are meant to do, and that's also part of the reason they are not part of the main RuboCop anymore.

@koic
Copy link
Member Author

koic commented Mar 29, 2021

I think about splitting this cop into two.
The two cases for filter_map are different in compatibility and readability for bad case and good case.

1. Performance/SelectMap cop

# bad
ary.select(&:foo).map(&:bar)
ary.filter(&:foo).map(&:bar)

# good
ary.filter_map { |o| o.bar if o.foo }
  • Bad case and good case are compatible behaviour.
  • Readability will change, but performance will be prioritized.
  • This cop will be renamed and updated this PR. And start the default from pending status, first.

2. Performance/MapCompact cop

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

# good
ary.filter_map(&:foo)
  • Bad case and good case are not compatible behaviour. In other words, it means that safe auto-correction is not possible.
  • It will reduce compact method call and improve readability.
  • I will open and implement a new PR.

Cc @rubocop/rubocop-core

koic added a commit to koic/rubocop-performance that referenced this pull request Mar 29, 2021
Follow rubocop#211 (comment)

This PR adds `Performance/MapCompact` cop

In Ruby 2.7, `Enumerable#filter_map` has been added.

This cop identifies places where `select.map` can be replaced by `filter_map`.
It is marked as unsafe auto-correction by default because `map { ... }.compact`
that is not compatible with `filter_map`.

The following is good and bad cases.

```ruby
# bad
ary.map(&:foo).compact
ary.select(&:foo).compact

# good
ary.filter_map(&:foo)
ary.map(&:foo).compact!
```
@koic koic mentioned this pull request Mar 29, 2021
8 tasks
koic added a commit to koic/rubocop-performance that referenced this pull request Mar 29, 2021
Follow rubocop#211 (comment)

This PR adds `Performance/MapCompact` cop

In Ruby 2.7, `Enumerable#filter_map` has been added.

This cop identifies places where `map { ... }.compact` can be replaced by `filter_map`.
It is marked as unsafe auto-correction by default because `map { ... }.compact`
that is not compatible with `filter_map`.

The following is good and bad cases.

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

# good
ary.filter_map(&:foo)
ary.map(&:foo).compact!
```
koic added a commit to koic/rubocop-performance that referenced this pull request Mar 29, 2021
Follow rubocop#211 (comment)

This PR adds `Performance/MapCompact` cop

In Ruby 2.7, `Enumerable#filter_map` has been added.

This cop identifies places where `map { ... }.compact` can be replaced by `filter_map`.
It is marked as unsafe auto-correction by default because `map { ... }.compact`
that is not compatible with `filter_map`.

The following is good and bad cases.

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

# good
ary.filter_map(&:foo)
ary.map(&:foo).compact!
```
@koic koic force-pushed the add_new_performance_filter_map_cop branch from 28b6369 to f31af62 Compare April 12, 2021 04:34
@koic
Copy link
Member Author

koic commented Apr 12, 2021

The cop have been split into Performance/SelectMap cop (this PR) and Performance/MapCompact cop (#229), renamed to Performance/SelectMap cop, and changed to pending status.

@koic koic changed the title [Fix #178] Add new Performance/FilterMap cop [Fix #178] Add new Performance/SelectMap cop Apr 12, 2021
@Drenmi
Copy link
Contributor

Drenmi commented Apr 12, 2021

I agree performance cops should be disabled by default, also because many of them depend on VM implementation details, so they may sometimes be slower on a different Ruby version from which it was first tested. As far as I know, no-one keeps track of these micro-optimizations across Ruby versions.

@koic
Copy link
Member Author

koic commented Apr 12, 2021

many of them depend on VM implementation details, so they may sometimes be slower on a different Ruby version from which it was first tested. As far as I know, no-one keeps track of these micro-optimizations across Ruby versions.

Sure. Since the performance result of Performance cops depends on Ruby implementations, I think it would be better to add to documentation that it is at least based on CRuby.

I agree performance cops should be disabled by default

Which the Performance cops does this refer to? If all the Performance cops were pointed to, rubocop-performance gem itself would be meaningless 😅 I think it will be considered individually whether it can be disabled by default.

To return to this PR topic :-)
I think the Performance/SelectMap cop can be disabled by default again as performance improvements are unclear for user impact. Also, the cop does not have auto-correction, conversion is a little difficult.
On the other hand, Performance/MapCompact cop has unsafe auto-correction and will not compromise readability, so it can be pending by default.

koic added a commit to koic/rubocop-performance that referenced this pull request Apr 12, 2021
Follow rubocop#211 (comment)

This PR adds `Performance/MapCompact` cop

In Ruby 2.7, `Enumerable#filter_map` has been added.

This cop identifies places where `map { ... }.compact` can be replaced by `filter_map`.
It is marked as unsafe auto-correction by default because `map { ... }.compact`
that is not compatible with `filter_map`.

The following is good and bad cases.

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

# good
ary.filter_map(&:foo)
ary.map(&:foo).compact!
```
Fixes rubocop#178 and rubocop#193.

This PR adds new `Performance/SelectMap` cop.

In Ruby 2.7, `Enumerable#filter_map` has been added.

This cop identifies places where `select.map` can be replaced by `filter_map`.

The following is good and bad cases.

```ruby
# bad
ary.select(&:foo).map(&:bar)
ary.filter(&:foo).map(&:bar)

# good
ary.filter_map { |o| o.bar if o.foo }
```
@koic koic force-pushed the add_new_performance_filter_map_cop branch from f31af62 to f267ffe Compare April 17, 2021 12:21
@koic koic merged commit 6c5c8e3 into rubocop:master Apr 18, 2021
@koic koic deleted the add_new_performance_filter_map_cop branch April 18, 2021 14:30
patrickm53 pushed a commit to patrickm53/performance-develop-rubyonrails that referenced this pull request Sep 23, 2022
Follow rubocop/rubocop-performance#211 (comment)

This PR adds `Performance/MapCompact` cop

In Ruby 2.7, `Enumerable#filter_map` has been added.

This cop identifies places where `map { ... }.compact` can be replaced by `filter_map`.
It is marked as unsafe auto-correction by default because `map { ... }.compact`
that is not compatible with `filter_map`.

The following is good and bad cases.

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

# good
ary.filter_map(&:foo)
ary.map(&:foo).compact!
```
richardstewart0213 added a commit to richardstewart0213/performance-build-Performance-optimization-analysis- that referenced this pull request Nov 4, 2022
Follow rubocop/rubocop-performance#211 (comment)

This PR adds `Performance/MapCompact` cop

In Ruby 2.7, `Enumerable#filter_map` has been added.

This cop identifies places where `map { ... }.compact` can be replaced by `filter_map`.
It is marked as unsafe auto-correction by default because `map { ... }.compact`
that is not compatible with `filter_map`.

The following is good and bad cases.

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

# good
ary.filter_map(&:foo)
ary.map(&:foo).compact!
```
Cute0110 added a commit to Cute0110/Rubocop-Performance that referenced this pull request Sep 28, 2023
Follow rubocop/rubocop-performance#211 (comment)

This PR adds `Performance/MapCompact` cop

In Ruby 2.7, `Enumerable#filter_map` has been added.

This cop identifies places where `map { ... }.compact` can be replaced by `filter_map`.
It is marked as unsafe auto-correction by default because `map { ... }.compact`
that is not compatible with `filter_map`.

The following is good and bad cases.

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

# good
ary.filter_map(&:foo)
ary.map(&:foo).compact!
```
SerhiiMisiura added a commit to SerhiiMisiura/Rubocop-Performance that referenced this pull request Oct 5, 2023
Follow rubocop/rubocop-performance#211 (comment)

This PR adds `Performance/MapCompact` cop

In Ruby 2.7, `Enumerable#filter_map` has been added.

This cop identifies places where `map { ... }.compact` can be replaced by `filter_map`.
It is marked as unsafe auto-correction by default because `map { ... }.compact`
that is not compatible with `filter_map`.

The following is good and bad cases.

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

# good
ary.filter_map(&:foo)
ary.map(&:foo).compact!
```
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.

Feature request: encourage Enumerable#filter_map
4 participants