Skip to content

Commit 15d020b

Browse files
committed
feat: add avoid-reverse rule (fixes #12)
1 parent f104f4c commit 15d020b

File tree

4 files changed

+160
-2
lines changed

4 files changed

+160
-2
lines changed

README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Rules for Array functions and methods.
1515
- [Examples](#examples-1)
1616
- [`prefer-array-from`](#prefer-array-from)
1717
- [Examples](#examples-2)
18+
- [`avoid-reverse`](#avoid-reverse)
19+
- [Examples](#examples-3)
1820
- [`array-func/recommended` Configuration](#array-funcrecommended-configuration)
1921
- [Using the Configuration](#using-the-configuration)
2022
- [License](#license)
@@ -151,6 +153,42 @@ const arrayCopy = Array.from(array);
151153
const characterArray = Array.from("string");
152154
```
153155

156+
### `avoid-reverse`
157+
Avoid reversing the array and running a method on it if there is an equivalent
158+
of the method operating on the array from the other end.
159+
160+
There are two operations with such equivalents: `indexOf` with `lastIndexOf` and
161+
`reduce` with `reduceRight`.
162+
163+
This rule is auto fixable.
164+
165+
#### Examples
166+
Code that triggers this rule:
167+
```js
168+
const lastIndex = array.reverse().indexOf(1);
169+
170+
const firstIndex = array.reverse().lastIndexOf(1);
171+
172+
const sum = array.reverse().reduce((p, c) => p + c, 0);
173+
174+
const reverseSum = array.reverse().reduceRight((p, c) => p + c, 0);
175+
```
176+
177+
Code that doesn't trigger this rule:
178+
```js
179+
const lastIndex = array.lastIndexOf(1);
180+
181+
const firstIndex = array.indexOf(1);
182+
183+
const sum = array.reduce((p, c) => p + c, 0);
184+
185+
const reverseSum = array.reduceRight((p, c) => p + c, 0);
186+
187+
const reverseArray = array.reverse();
188+
189+
const reverseMap = array.reverse().map((r) => r + 1);
190+
```
191+
154192
## `array-func/recommended` Configuration
155193
The recommended configuration will set your parser ECMA Version to 2015, since that's when the Array functions and methods were added.
156194

@@ -159,6 +197,7 @@ Rule | Error level | Fixable
159197
`from-map` | Error | Yes
160198
`no-unnecessary-this-arg` | Error | Sometimes
161199
`prefer-array-from` | Error | Yes
200+
`avoid-reverse` | Error | Yes
162201

163202
### Using the Configuration
164203
To enable this configuration use the `extends` property in your `.eslintrc.json` config file (may look different for other config file styles):

index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ module.exports = {
88
rules: {
99
"from-map": require("./rules/from-map"),
1010
"no-unnecessary-this-arg": require("./rules/no-unnecessary-this-arg"),
11-
"prefer-array-from": require("./rules/prefer-array-from")
11+
"prefer-array-from": require("./rules/prefer-array-from"),
12+
"avoid-reverse": require("./rules/avoid-reverse")
1213
},
1314
configs: {
1415
recommended: {
@@ -19,7 +20,8 @@ module.exports = {
1920
rules: {
2021
"array-func/from-map": "error",
2122
"array-func/no-unnecessary-this-arg": "error",
22-
"array-func/prefer-array-from": "error"
23+
"array-func/prefer-array-from": "error",
24+
"array-func/avoid-reverse": "error"
2325
}
2426
}
2527
}

rules/avoid-reverse.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* @author Martin Giger
3+
* @license MIT
4+
*/
5+
"use strict";
6+
7+
const {
8+
isMethod,
9+
getParent
10+
} = require("../lib/helpers/call-expression");
11+
12+
const REPLACEMENTS = {
13+
indexOf: "lastIndexOf",
14+
reduce: "reduceRight",
15+
lastIndexOf: "indexOf",
16+
reduceRight: "reduce"
17+
};
18+
19+
module.exports = {
20+
meta: {
21+
docs: {
22+
description: "Prefer methods operating from the right over reversing the array",
23+
recommended: true
24+
},
25+
schema: [],
26+
fixable: "code"
27+
},
28+
create(context) {
29+
return {
30+
"CallExpression:exit"(node) {
31+
if(Object.keys(REPLACEMENTS).every((m) => !isMethod(node, m))) {
32+
return;
33+
}
34+
35+
const parent = getParent(node);
36+
if(!isMethod(parent, 'reverse')) {
37+
return;
38+
}
39+
40+
const reversed = REPLACEMENTS[node.callee.property.name];
41+
42+
context.report({
43+
node: node.callee.property,
44+
loc: {
45+
start: parent.callee.property.loc.start,
46+
end: node.callee.property.loc.end
47+
},
48+
message: `Prefer using ${reversed} over reversing the array and ${node.callee.property.name}`,
49+
fix(fixer) {
50+
return fixer.replaceTextRange([
51+
parent.callee.property.start,
52+
node.callee.property.end
53+
], reversed);
54+
}
55+
});
56+
}
57+
};
58+
}
59+
};

test/rules/avoid-reverse.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import test from 'ava';
2+
import AvaRuleTester from 'eslint-ava-rule-tester';
3+
import rule from '../../rules/avoid-reverse';
4+
5+
const ruleTester = new AvaRuleTester(test, {
6+
parserOptions: {
7+
ecmaVersion: 2015
8+
}
9+
});
10+
11+
ruleTester.run('from-map', rule, {
12+
valid: [
13+
'array.lastIndexOf(1)',
14+
'array.indexOf(1)',
15+
'array.reduce((p, c) => p + c, 0)',
16+
'array.reduceRight((p, c) => p + c, 0)',
17+
'array.reverse()',
18+
'array.reverse().map((r) => r + 1)'
19+
],
20+
invalid: [
21+
{
22+
code: 'array.reverse().indexOf(1)',
23+
errors: [ {
24+
message: 'Prefer using lastIndexOf over reversing the array and indexOf',
25+
column: 7,
26+
line: 1
27+
} ],
28+
output: 'array.lastIndexOf(1)'
29+
},
30+
{
31+
code: 'array.reverse().lastIndexOf(1)',
32+
errors: [ {
33+
message: 'Prefer using indexOf over reversing the array and lastIndexOf',
34+
column: 7,
35+
line: 1
36+
} ],
37+
output: 'array.indexOf(1)'
38+
},
39+
{
40+
code: 'array.reverse().reduce((p, c) => p + c, 0)',
41+
errors: [ {
42+
message: 'Prefer using reduceRight over reversing the array and reduce',
43+
column: 7,
44+
line: 1
45+
} ],
46+
output: 'array.reduceRight((p, c) => p + c, 0)'
47+
},
48+
{
49+
code: 'array.reverse().reduceRight((p, c) => p + c, 0)',
50+
errors: [ {
51+
message: 'Prefer using reduce over reversing the array and reduceRight',
52+
column: 7,
53+
line: 1
54+
} ],
55+
output: 'array.reduce((p, c) => p + c, 0)'
56+
}
57+
]
58+
});

0 commit comments

Comments
 (0)