fast-equals 2.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,12 +1,54 @@
1
1
  # fast-equals CHANGELOG
2
2
 
3
+ ## 3.0.0
4
+
5
+ ### Breaking changes
6
+
7
+ When creating a custom equality comparator via `createCustomEqual`, the equality method has an expanded contract:
8
+
9
+ ```ts
10
+ // Before
11
+ type EqualityComparator = (objectA: any, objectB: any, meta: any) => boolean;
12
+
13
+ // After
14
+ type InternalEqualityComparator = (
15
+ objectA: any,
16
+ objectB: any,
17
+ indexOrKeyA: any,
18
+ indexOrKeyB: any,
19
+ parentA: any,
20
+ parentB: any,
21
+ meta: any,
22
+ ) => boolean;
23
+ ```
24
+
25
+ If you have a custom equality comparator, you can ignore the differences by just passing additional `undefined` parameters, or you can use the parameters to further improve / clarify the logic.
26
+
27
+ - Add [#57](https://github.com/planttheidea/fast-equals/pull/57) - support additional metadata for custom equality comparators
28
+
29
+ ## 2.0.4
30
+
31
+ - Fix [#58](https://github.com/planttheidea/fast-equals/issues/58) - duplicate entries in `Map` / `Set` can create false equality success
32
+ - [#60](https://github.com/planttheidea/fast-equals/issues/60) - Add documentation for key equality of `Map` being a part of `deepEqual`
33
+
34
+ ## 2.0.3
35
+
36
+ - Fix [#50](https://github.com/planttheidea/fast-equals/pull/50) - copy-pasta in cacheable check
37
+
38
+ ## 2.0.2
39
+
40
+ - Optimize iterables comparisons to not double-iterate
41
+ - Optimize loop-based comparisons for speed
42
+ - Improve cache handling in circular handlers
43
+ - Improve stability of memory by reducing variable instantiation
44
+
3
45
  ## 2.0.1
4
46
 
5
47
  - Fix [#41](https://github.com/planttheidea/fast-equals/pull/41) - prevent `.rpt2_cache` directory from being published for better CI environment support (thanks [@herberttn](https://github.com/herberttn))
6
48
 
7
49
  ## 2.0.0
8
50
 
9
- #### Breaking changes
51
+ ### Breaking changes
10
52
 
11
53
  - There are longer `fast-equals/es`, `fast-equals/lib`, `fast-equals/mjs` locations
12
54
  - Instead, there are 3 builds in `dist` for different consumption types:
@@ -16,13 +58,13 @@
16
58
  - There is no default export anymore, only the previously-existing named exports
17
59
  - To get all into a namespace, use `import * as fe from 'fast-equals`
18
60
 
19
- #### Updates
61
+ ### Updates
20
62
 
21
63
  - Rewritten completely in TypeScript
22
64
  - Improve speed of `Map` / `Set` comparisons
23
65
  - Improve speed of React element comparisons
24
66
 
25
- #### Fixes
67
+ ### Fixes
26
68
 
27
69
  - Consider pure objects (`Object.create(null)`) to be plain objects
28
70
  - Fix typings for `createCustomEqual`
@@ -122,3 +164,7 @@
122
164
  ## 1.0.0
123
165
 
124
166
  - Initial release
167
+
168
+ ```
169
+
170
+ ```
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  <img src="https://img.shields.io/badge/coverage-100%25-brightgreen.svg"/>
5
5
  <img src="https://img.shields.io/badge/license-MIT-blue.svg"/>
6
6
 
7
- Perform [blazing fast](#benchmarks) equality comparisons (either deep or shallow) on two objects passed. It has no dependencies, and is ~1.3kB when minified and gzipped.
7
+ Perform [blazing fast](#benchmarks) equality comparisons (either deep or shallow) on two objects passed. It has no dependencies, and is ~1kB when minified and gzipped.
8
8
 
9
9
  Unlike most equality validation libraries, the following types are handled out-of-the-box:
10
10
 
@@ -19,17 +19,20 @@ Starting with version `1.5.0`, circular objects are supported for both deep and
19
19
 
20
20
  ## Table of contents
21
21
 
22
- - [Usage](#usage)
23
- - [Specific builds](#specific-builds)
24
- - [Available methods](#available-methods)
25
- - [deepEqual](#deepequal)
26
- - [shallowEqual](#shallowequal)
27
- - [sameValueZeroEqual](#samevaluezeroequal)
28
- - [circularDeepEqual](#circulardeepequal)
29
- - [circularShallowEqual](#circularshallowequal)
30
- - [createCustomEqual](#createcustomequal)
31
- - [Benchmarks](#benchmarks)
32
- - [Development](#development)
22
+ - [fast-equals](#fast-equals)
23
+ - [Table of contents](#table-of-contents)
24
+ - [Usage](#usage)
25
+ - [Specific builds](#specific-builds)
26
+ - [Available methods](#available-methods)
27
+ - [deepEqual](#deepequal)
28
+ - [Comparing `Map`s](#comparing-maps)
29
+ - [shallowEqual](#shallowequal)
30
+ - [sameValueZeroEqual](#samevaluezeroequal)
31
+ - [circularDeepEqual](#circulardeepequal)
32
+ - [circularShallowEqual](#circularshallowequal)
33
+ - [createCustomEqual](#createcustomequal)
34
+ - [Benchmarks](#benchmarks)
35
+ - [Development](#development)
33
36
 
34
37
  ## Usage
35
38
 
@@ -49,7 +52,7 @@ import * as fe from 'fast-equals';
49
52
  console.log(fe.deep({ foo: 'bar' }, { foo: 'bar' })); // true
50
53
  ```
51
54
 
52
- #### Specific builds
55
+ ### Specific builds
53
56
 
54
57
  There are three builds, an ESM build for modern build systems / runtimes, a CommonJS build for traditional NodeJS environments, and a UMD build for legacy implementations. The ideal one will likely be chosen for you automatically, however if you want to use a specific build you can always import it directly:
55
58
 
@@ -64,7 +67,7 @@ There is also a pre-minified version of the UMD build available:
64
67
 
65
68
  ## Available methods
66
69
 
67
- #### deepEqual
70
+ ### deepEqual
68
71
 
69
72
  Performs a deep equality comparison on the two objects passed and returns a boolean representing the value equivalency of the objects.
70
73
 
@@ -78,7 +81,20 @@ console.log(objectA === objectB); // false
78
81
  console.log(deepEqual(objectA, objectB)); // true
79
82
  ```
80
83
 
81
- #### shallowEqual
84
+ #### Comparing `Map`s
85
+
86
+ `Map` objects support complex keys (objects, Arrays, etc.), however [the spec for key lookups in `Map` are based on `SameZeroValue`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#key_equality). If the spec were followed for comparison, the following would always be `false`:
87
+
88
+ ```javascript
89
+ const mapA = new Map([[{ foo: 'bar' }, { baz: 'quz' }]]);
90
+ const mapB = new Map([[{ foo: 'bar' }, { baz: 'quz' }]]);
91
+
92
+ deepEqual(mapA, mapB);
93
+ ```
94
+
95
+ To support true deep equality of all contents, `fast-equals` will perform a deep equality comparison for key and value parirs. Therefore, the above would be `true`.
96
+
97
+ ### shallowEqual
82
98
 
83
99
  Performs a shallow equality comparison on the two objects passed and returns a boolean representing the value equivalency of the objects.
84
100
 
@@ -96,7 +112,7 @@ console.log(shallowEqual(objectA, objectB)); // true
96
112
  console.log(shallowEqual(objectA, objectC)); // false
97
113
  ```
98
114
 
99
- #### sameValueZeroEqual
115
+ ### sameValueZeroEqual
100
116
 
101
117
  Performs a [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) comparison on the two objects passed and returns a boolean representing the value equivalency of the objects. In simple terms, this means either strictly equal or both `NaN`.
102
118
 
@@ -114,7 +130,7 @@ console.log(sameValueZeroEqual(mainObject.foo, objectB)); // true
114
130
  console.log(sameValueZeroEqual(mainObject, objectC)); // false
115
131
  ```
116
132
 
117
- #### circularDeepEqual
133
+ ### circularDeepEqual
118
134
 
119
135
  Performs the same comparison as `deepEqual` but supports circular objects. It is slower than `deepEqual`, so only use if you know circular objects are present.
120
136
 
@@ -134,7 +150,9 @@ console.log(circularDeepEqual(new Circular('foo'), new Circular('foo'))); // tru
134
150
  console.log(circularDeepEqual(new Circular('foo'), new Circular('bar'))); // false
135
151
  ```
136
152
 
137
- #### circularShallowEqual
153
+ Just as with `deepEqual`, [both keys and values are compared for deep equality](#comparing-maps).
154
+
155
+ ### circularShallowEqual
138
156
 
139
157
  Performs the same comparison as `shallowequal` but supports circular objects. It is slower than `shallowEqual`, so only use if you know circular objects are present.
140
158
 
@@ -147,7 +165,7 @@ console.log(circularShallowEqual(array, ['foo', array])); // true
147
165
  console.log(circularShallowEqual(array, [array])); // false
148
166
  ```
149
167
 
150
- #### createCustomEqual
168
+ ### createCustomEqual
151
169
 
152
170
  Creates a custom equality comparator that will be used on nested values in the object. Unlike `deepEqual` and `shallowEqual`, this is a partial-application function that will receive the internal comparator and should return a function that compares two objects.
153
171
 
@@ -155,16 +173,19 @@ The signature is as follows:
155
173
 
156
174
  ```typescript
157
175
  type EqualityComparator = (a: any, b: any, meta?: any) => boolean;
176
+ type InternalEqualityComparator = (a: any, b: any, indexOrKeyA: any, indexOrKeyB: any, parentA: any, parentB: any, meta: any) => boolean;
158
177
  type EqualityComparatorCreator = (
159
178
  deepEqual: EqualityComparator,
160
- ) => EqualityComparator;
179
+ ) => InternalEqualityComparator;
161
180
 
162
181
  function createCustomEqual(
163
182
  createIsEqual?: EqualityComparatorCreator,
164
183
  ): EqualityComparator;
165
184
  ```
166
185
 
167
- The `meta` parameter in `EqualityComparator` is whatever you want it to be. It will be passed through to all equality checks, and is meant specifically for use with custom equality methods. For example, with the `circularDeepEqual` and `circularShallowEqual` methods, it is used to pass through a cache of processed objects.
186
+ The `meta` parameter in `EqualityComparator` and `InternalEqualityComparator` is whatever you want it to be. It will be passed through to all equality checks, and is meant specifically for use with custom equality methods. For example, with the `circularDeepEqual` and `circularShallowEqual` methods, it is used to pass through a cache of processed objects.
187
+
188
+ _**NOTE**: `Map` implementations compare equality for both keys and value. When using a custom comparator and comparing equality of the keys, the iteration index is provided as both `indexOrKeyA` and `indexOrKeyB` to help use-cases where ordering of keys matters to equality._
168
189
 
169
190
  An example for a custom equality comparison that also checks against values in the meta object:
170
191
 
@@ -172,11 +193,10 @@ An example for a custom equality comparison that also checks against values in t
172
193
  import { createCustomEqual } from 'fast-equals';
173
194
 
174
195
  const isDeepEqualOrFooMatchesMeta = createCustomEqual(
175
- (deepEqual) => (objectA, objectB, meta) => (
196
+ (deepEqual) => (objectA, objectB, indexOrKeyA, indexOrKeyB, parentA, parentB, meta) =>
176
197
  objectA.foo === meta ||
177
198
  objectB.foo === meta ||
178
- deepEqual(objectA, objectB, meta)
179
- )
199
+ deepEqual(objectA, objectB, meta),
180
200
  );
181
201
 
182
202
  const objectA = { foo: 'bar' };
@@ -188,7 +208,7 @@ console.log(isDeepEqualOrFooMatchesMeta(objectA, objectB, meta)); // true
188
208
 
189
209
  ## Benchmarks
190
210
 
191
- All benchmarks were performed on an i7 8-core Arch Linux laptop with 16GB of memory using NodeJS version `10.15.3`, and are based on averages of running comparisons based deep equality on the following object types:
211
+ All benchmarks were performed on an i7-8650U Ubuntu Linux laptop with 24GB of memory using NodeJS version `12.19.1`, and are based on averages of running comparisons based deep equality on the following object types:
192
212
 
193
213
  - Primitives (`String`, `Number`, `null`, `undefined`)
194
214
  - `Function`
@@ -201,20 +221,21 @@ All benchmarks were performed on an i7 8-core Arch Linux laptop with 16GB of mem
201
221
 
202
222
  | | Operations / second |
203
223
  | -------------------------- | ------------------- |
204
- | **fast-equals** | **142,730** |
205
- | nano-equal | 115,530 |
206
- | shallow-equal-fuzzy | 102,633 |
207
- | fast-deep-equal | 102,335 |
208
- | react-fast-compare | 100,036 |
209
- | **fast-equals (circular)** | **79,589** |
210
- | underscore.isEqual | 63,390 |
211
- | deep-equal | 48,783 |
212
- | lodash.isEqual | 24,456 |
213
- | deep-eql | 24,196 |
214
- | assert.deepStrictEqual | 1,382 |
224
+ | **fast-equals** | **153,880** |
225
+ | fast-deep-equal | 144,035 |
226
+ | react-fast-compare | 130,324 |
227
+ | nano-equal | 104,624 |
228
+ | **fast-equals (circular)** | **97,610** |
229
+ | shallow-equal-fuzzy | 83,946 |
230
+ | underscore.isEqual | 47,370 |
231
+ | lodash.isEqual | 25,053 |
232
+ | deep-eql | 22,146 |
233
+ | assert.deepStrictEqual | 532 |
234
+ | deep-equal | 209 |
215
235
 
216
236
  Caveats that impact the benchmark (and accuracy of comparison):
217
237
 
238
+ - `Map`s, `Promise`s, and `Set`s were excluded from the benchmark entirely because no library other than `deep-eql` fully supported their comparison
218
239
  - `assert.deepStrictEqual` does not support `NaN` or `SameValueZero` equality for dates
219
240
  - `deep-eql` does not support `SameValueZero` equality for zero equality (positive and negative zero are not equal)
220
241
  - `deep-equal` does not support `NaN` and does not strictly compare object type, or date / regexp values, nor uses `SameValueZero` equality for dates
@@ -224,7 +245,7 @@ Caveats that impact the benchmark (and accuracy of comparison):
224
245
  - `shallow-equal-fuzzy` does not strictly compare object type or regexp values, nor `SameValueZero` equality for dates
225
246
  - `underscore.isEqual` does not support `SameValueZero` equality for primitives or dates
226
247
 
227
- All of these have the potential of inflating the respective library's numbers in comparison to `fast-equals`, but it was the closest apples-to-apples comparison I could create of a reasonable sample size. `Map`s, `Promise`s, and `Set`s were excluded from the benchmark entirely because no library other than `deep-eql` fully supported their comparison. The same logic applies to `react` elements (which can be circular objects), but simple elements are non-circular objects so I kept the `react` comparison very basic to allow it to be included.
248
+ All of these have the potential of inflating the respective library's numbers in comparison to `fast-equals`, but it was the closest apples-to-apples comparison I could create of a reasonable sample size. It should be noted that `react` elements can be circular objects, however simple elements are not; I kept the `react` comparison very basic to allow it to be included.
228
249
 
229
250
  ## Development
230
251