fast-equals 5.0.0-beta.5 → 5.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 +17 -1
- package/README.md +68 -48
- package/dist/cjs/index.cjs +165 -140
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/types/comparator.d.ts +20 -3
- package/dist/cjs/types/equals.d.ts +5 -2
- package/dist/cjs/types/index.d.ts +9 -9
- package/dist/cjs/types/internalTypes.d.ts +100 -12
- package/dist/cjs/types/utils.d.ts +1 -6
- package/dist/esm/index.mjs +165 -140
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/types/comparator.d.ts +20 -3
- package/dist/esm/types/equals.d.ts +5 -2
- package/dist/esm/types/index.d.ts +9 -9
- package/dist/esm/types/internalTypes.d.ts +100 -12
- package/dist/esm/types/utils.d.ts +1 -6
- package/dist/min/index.js +1 -1
- package/dist/min/types/comparator.d.ts +20 -3
- package/dist/min/types/equals.d.ts +5 -2
- package/dist/min/types/index.d.ts +9 -9
- package/dist/min/types/internalTypes.d.ts +100 -12
- package/dist/min/types/utils.d.ts +1 -6
- package/dist/umd/index.js +165 -140
- package/dist/umd/index.js.map +1 -1
- package/dist/umd/types/comparator.d.ts +20 -3
- package/dist/umd/types/equals.d.ts +5 -2
- package/dist/umd/types/index.d.ts +9 -9
- package/dist/umd/types/internalTypes.d.ts +100 -12
- package/dist/umd/types/utils.d.ts +1 -6
- package/package.json +7 -7
- package/recipes/legacy-circular-equal-support.md +3 -2
- package/recipes/legacy-regexp-support.md +2 -3
- package/recipes/non-standard-properties.md +4 -2
- package/src/comparator.ts +126 -38
- package/src/equals.ts +97 -63
- package/src/index.ts +21 -65
- package/src/internalTypes.ts +104 -16
- package/src/utils.ts +2 -24
package/CHANGELOG.md
CHANGED
|
@@ -4,14 +4,30 @@
|
|
|
4
4
|
|
|
5
5
|
### Breaking changes
|
|
6
6
|
|
|
7
|
+
#### `constructor` equality now required
|
|
8
|
+
|
|
9
|
+
To align with other implementations common in the community, but also to be more functionally correct, the two objects being compared now must have equal `constructor`s.
|
|
10
|
+
|
|
11
|
+
#### `Map` / `Set` comparisons no longer support IE11
|
|
12
|
+
|
|
13
|
+
In previous verisons, `.forEach()` was used to ensure that support for `Symbol` was not required, as IE11 did not have `Symbol` and therefore both `Map` and `Set` did not have iterator-based methods such as `.values()` or `.entries()`. Since IE11 is no longer a supported browser, and support for those methods is present in all browsers and Node for quite a while, the comparison has moved to use these methods. This results in a ~20% performance increase.
|
|
14
|
+
|
|
7
15
|
#### `createCustomEqual` contract has changed
|
|
8
16
|
|
|
9
|
-
To better facilitate strict comparisons, but also to allow for `meta` use separate from caching, the contract for `createCustomEqual` has changed. See the [README documentation](./README.md#createcustomequal) for more details
|
|
17
|
+
To better facilitate strict comparisons, but also to allow for `meta` use separate from caching, the contract for `createCustomEqual` has changed. See the [README documentation](./README.md#createcustomequal) for more details, but froma high-level:
|
|
18
|
+
|
|
19
|
+
- `meta` is no longer passed through to equality comparators, but rather a general `state` object which contains `meta`
|
|
20
|
+
- `cache` now also lives on the `state` object, which allows for use of the `meta` property separate from but in parallel with the circular cache
|
|
21
|
+
- `equals` is now on `state`, which prevents the need to pass through the separate `isEqual` method for the equality comparator
|
|
10
22
|
|
|
11
23
|
#### `createCustomCircularEqual` has been removed
|
|
12
24
|
|
|
13
25
|
You can create a custom circular equality comparator through `createCustomEqual` now by providing `circular: true` to the options.
|
|
14
26
|
|
|
27
|
+
#### Custom `meta` values are no longer passed at callsite
|
|
28
|
+
|
|
29
|
+
To use `meta` properties for comparisons, they must be returned in a `createState` method.
|
|
30
|
+
|
|
15
31
|
#### Deep links have changed
|
|
16
32
|
|
|
17
33
|
If you were deep-linking into a specific asset type (ESM / CJS / UMD), they have changed location.
|
package/README.md
CHANGED
|
@@ -4,18 +4,21 @@
|
|
|
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.
|
|
7
|
+
Perform [blazing fast](#benchmarks) equality comparisons (either deep or shallow) on two objects passed, while also maintaining a high degree of flexibility for various implementation use-cases. It has no dependencies, and is ~1.8kB when minified and gzipped.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The following types are handled out-of-the-box:
|
|
10
10
|
|
|
11
|
-
- `
|
|
11
|
+
- Plain objects (including `react` elements and `Arguments`)
|
|
12
|
+
- Arrays
|
|
13
|
+
- Typed Arrays
|
|
12
14
|
- `Date` objects
|
|
13
15
|
- `RegExp` objects
|
|
14
16
|
- `Map` / `Set` iterables
|
|
15
17
|
- `Promise` objects
|
|
16
|
-
- `
|
|
18
|
+
- Primitive wrappers (`new Boolean()` / `new Number()` / `new String()`)
|
|
19
|
+
- Custom class instances, including subclasses of native classes
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
Methods are available for deep, shallow, or referential equality comparison. In addition, you can opt into support for circular objects, or performing a "strict" comparison with unconventional property definition, or both. You can also customize any specific type comparison based on your application's use-cases.
|
|
19
22
|
|
|
20
23
|
## Table of contents
|
|
21
24
|
|
|
@@ -162,7 +165,7 @@ console.log(circularShallowEqual(array, [array])); // false
|
|
|
162
165
|
|
|
163
166
|
Performs the same comparison as `deepEqual` but performs a strict comparison of the objects. In this includes:
|
|
164
167
|
|
|
165
|
-
- Checking
|
|
168
|
+
- Checking symbol properties
|
|
166
169
|
- Checking non-enumerable properties in object comparisons
|
|
167
170
|
- Checking full descriptor of properties on the object to match
|
|
168
171
|
- Checking non-index properties on arrays
|
|
@@ -269,24 +272,13 @@ Creates a custom equality comparator that will be used on nested values in the o
|
|
|
269
272
|
The signature is as follows:
|
|
270
273
|
|
|
271
274
|
```ts
|
|
272
|
-
interface
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
interface DefaultState<Meta> {
|
|
277
|
-
readonly cache: undefined | BaseCircular;
|
|
278
|
-
readonly equals: InternalEqualityComparator<Meta>;
|
|
279
|
-
meta: Meta;
|
|
280
|
-
readonly strict: boolean;
|
|
275
|
+
interface Cache<Key extends object, Value> {
|
|
276
|
+
delete(key: Key): boolean;
|
|
277
|
+
get(key: Key): Value | undefined;
|
|
278
|
+
set(key: Key, value: any): any;
|
|
281
279
|
}
|
|
282
280
|
|
|
283
|
-
|
|
284
|
-
a: A,
|
|
285
|
-
b: B,
|
|
286
|
-
state: State<Meta>,
|
|
287
|
-
) => boolean;
|
|
288
|
-
|
|
289
|
-
interface CreateComparatorCreatorOptions<Meta> {
|
|
281
|
+
interface ComparatorConfig<Meta> {
|
|
290
282
|
areArraysEqual: TypeEqualityComparator<any[], Meta>;
|
|
291
283
|
areDatesEqual: TypeEqualityComparator<Date, Meta>;
|
|
292
284
|
areMapsEqual: TypeEqualityComparator<Map<any, any>, Meta>;
|
|
@@ -300,20 +292,25 @@ interface CreateComparatorCreatorOptions<Meta> {
|
|
|
300
292
|
areTypedArraysEqual: TypeEqualityComparatory<TypedArray, Meta>;
|
|
301
293
|
}
|
|
302
294
|
|
|
303
|
-
|
|
295
|
+
function createCustomEqual<Meta>(options: {
|
|
304
296
|
circular?: boolean;
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
297
|
+
createCustomConfig?: (
|
|
298
|
+
defaultConfig: ComparatorConfig<Meta>,
|
|
299
|
+
) => Partial<ComparatorConfig<Meta>>;
|
|
300
|
+
createInternalComparator?: (
|
|
301
|
+
compare: <A, B>(a: A, b: B, state: State<Meta>) => boolean,
|
|
302
|
+
) => (
|
|
303
|
+
a: any,
|
|
304
|
+
b: any,
|
|
305
|
+
indexOrKeyA: any,
|
|
306
|
+
indexOrKeyB: any,
|
|
307
|
+
parentA: any,
|
|
308
|
+
parentB: any,
|
|
309
|
+
state: State<Meta>,
|
|
310
|
+
) => boolean;
|
|
311
|
+
createState?: () => { cache?: Cache; meta?: Meta };
|
|
311
312
|
strict?: boolean;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function createCustomEqual(
|
|
315
|
-
options: CustomEqualCreatorOptions<Meta>,
|
|
316
|
-
): <A, B>(a: A, b: B, metaOverride?: Meta) => boolean;
|
|
313
|
+
}): <A, B>(a: A, b: B, metaOverride?: Meta) => boolean;
|
|
317
314
|
```
|
|
318
315
|
|
|
319
316
|
Create a custom equality comparator. This allows complete control over building a bespoke equality method, in case your use-case requires a higher degree of performance, legacy environment support, or any other non-standard usage. The [recipes](#recipes) provide examples of use in different use-cases, but if you have a specific goal in mind and would like assistance feel free to [file an issue](https://github.com/planttheidea/fast-equals/issues).
|
|
@@ -333,7 +330,7 @@ Some recipes have been created to provide examples of use-cases for `createCusto
|
|
|
333
330
|
|
|
334
331
|
## Benchmarks
|
|
335
332
|
|
|
336
|
-
All benchmarks were performed on an
|
|
333
|
+
All benchmarks were performed on an i9-11900H Ubuntu Linux 22.04 laptop with 64GB of memory using NodeJS version `16.14.2`, and are based on averages of running comparisons based deep equality on the following object types:
|
|
337
334
|
|
|
338
335
|
- Primitives (`String`, `Number`, `null`, `undefined`)
|
|
339
336
|
- `Function`
|
|
@@ -344,19 +341,42 @@ All benchmarks were performed on an i7-8650U Ubuntu Linux laptop with 24GB of me
|
|
|
344
341
|
- `react` elements
|
|
345
342
|
- A mixed object with a combination of all the above types
|
|
346
343
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
344
|
+
```bash
|
|
345
|
+
Testing mixed objects equal...
|
|
346
|
+
┌─────────┬─────────────────────────────────┬────────────────┐
|
|
347
|
+
│ (index) │ Package │ Ops/sec │
|
|
348
|
+
├─────────┼─────────────────────────────────┼────────────────┤
|
|
349
|
+
│ 0 │ 'fast-equals' │ 1249567.730326 │
|
|
350
|
+
│ 1 │ 'fast-deep-equal' │ 1182463.587514 │
|
|
351
|
+
│ 2 │ 'react-fast-compare' │ 1152487.319161 │
|
|
352
|
+
│ 3 │ 'shallow-equal-fuzzy' │ 1092360.712389 │
|
|
353
|
+
│ 4 │ 'fast-equals (circular)' │ 676669.92003 │
|
|
354
|
+
│ 5 │ 'underscore.isEqual' │ 429430.837497 │
|
|
355
|
+
│ 6 │ 'lodash.isEqual' │ 237915.684734 │
|
|
356
|
+
│ 7 │ 'fast-equals (strict)' │ 181386.38032 │
|
|
357
|
+
│ 8 │ 'fast-equals (strict circular)' │ 156779.745875 │
|
|
358
|
+
│ 9 │ 'deep-eql' │ 139155.099209 │
|
|
359
|
+
│ 10 │ 'deep-equal' │ 1026.527229 │
|
|
360
|
+
└─────────┴─────────────────────────────────┴────────────────┘
|
|
361
|
+
|
|
362
|
+
Testing mixed objects not equal...
|
|
363
|
+
┌─────────┬─────────────────────────────────┬────────────────┐
|
|
364
|
+
│ (index) │ Package │ Ops/sec │
|
|
365
|
+
├─────────┼─────────────────────────────────┼────────────────┤
|
|
366
|
+
│ 0 │ 'fast-equals' │ 3255824.097237 │
|
|
367
|
+
│ 1 │ 'react-fast-compare' │ 2654721.726058 │
|
|
368
|
+
│ 2 │ 'fast-deep-equal' │ 2582218.974752 │
|
|
369
|
+
│ 3 │ 'fast-equals (circular)' │ 2474303.26566 │
|
|
370
|
+
│ 4 │ 'fast-equals (strict)' │ 1088066.604881 │
|
|
371
|
+
│ 5 │ 'fast-equals (strict circular)' │ 949253.614181 │
|
|
372
|
+
│ 6 │ 'nano-equal' │ 939170.554148 │
|
|
373
|
+
│ 7 │ 'underscore.isEqual' │ 738852.197879 │
|
|
374
|
+
│ 8 │ 'lodash.isEqual' │ 307306.622212 │
|
|
375
|
+
│ 9 │ 'deep-eql' │ 156250.110401 │
|
|
376
|
+
│ 10 │ 'assert.deepStrictEqual' │ 22839.454561 │
|
|
377
|
+
│ 11 │ 'deep-equal' │ 4034.45114 │
|
|
378
|
+
└─────────┴─────────────────────────────────┴────────────────┘
|
|
379
|
+
```
|
|
360
380
|
|
|
361
381
|
Caveats that impact the benchmark (and accuracy of comparison):
|
|
362
382
|
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -10,15 +10,6 @@ function combineComparators(comparatorA, comparatorB) {
|
|
|
10
10
|
return comparatorA(a, b, state) && comparatorB(a, b, state);
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
-
/**
|
|
14
|
-
* Default equality comparator pass-through, used as the standard `isEqual` creator for
|
|
15
|
-
* use inside the built comparator.
|
|
16
|
-
*/
|
|
17
|
-
function createInternalComparator(compare) {
|
|
18
|
-
return function (a, b, _indexOrKeyA, _indexOrKeyB, _parentA, _parentB, state) {
|
|
19
|
-
return compare(a, b, state);
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
13
|
/**
|
|
23
14
|
* Wrap the provided `areItemsEqual` method to manage the circular state, allowing
|
|
24
15
|
* for circular references to be safely included in the comparison without creating
|
|
@@ -91,36 +82,42 @@ function areDatesEqual(a, b) {
|
|
|
91
82
|
* Whether the `Map`s are equal in value.
|
|
92
83
|
*/
|
|
93
84
|
function areMapsEqual(a, b, state) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
85
|
+
if (a.size !== b.size) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
var matchedIndices = {};
|
|
89
|
+
var aIterable = a.entries();
|
|
90
|
+
var index = 0;
|
|
91
|
+
var aResult;
|
|
92
|
+
var bResult;
|
|
93
|
+
while ((aResult = aIterable.next())) {
|
|
94
|
+
if (aResult.done) {
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
var bIterable = b.entries();
|
|
98
|
+
var hasMatch = false;
|
|
99
|
+
var matchIndex = 0;
|
|
100
|
+
while ((bResult = bIterable.next())) {
|
|
101
|
+
if (bResult.done) {
|
|
102
|
+
break;
|
|
106
103
|
}
|
|
107
|
-
var
|
|
108
|
-
var
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
(
|
|
113
|
-
state.equals(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
104
|
+
var _a = aResult.value, aKey = _a[0], aValue = _a[1];
|
|
105
|
+
var _b = bResult.value, bKey = _b[0], bValue = _b[1];
|
|
106
|
+
if (!hasMatch &&
|
|
107
|
+
!matchedIndices[matchIndex] &&
|
|
108
|
+
(hasMatch =
|
|
109
|
+
state.equals(aKey, bKey, index, matchIndex, a, b, state) &&
|
|
110
|
+
state.equals(aValue, bValue, aKey, bKey, a, b, state))) {
|
|
111
|
+
matchedIndices[matchIndex] = true;
|
|
112
|
+
}
|
|
113
|
+
matchIndex++;
|
|
114
|
+
}
|
|
115
|
+
if (!hasMatch) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
index++;
|
|
122
119
|
}
|
|
123
|
-
return
|
|
120
|
+
return true;
|
|
124
121
|
}
|
|
125
122
|
/**
|
|
126
123
|
* Whether the objects are equal in value.
|
|
@@ -208,33 +205,40 @@ function areRegExpsEqual(a, b) {
|
|
|
208
205
|
* Whether the `Set`s are equal in value.
|
|
209
206
|
*/
|
|
210
207
|
function areSetsEqual(a, b, state) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
208
|
+
if (a.size !== b.size) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
var matchedIndices = {};
|
|
212
|
+
var aIterable = a.values();
|
|
213
|
+
var aResult;
|
|
214
|
+
var bResult;
|
|
215
|
+
while ((aResult = aIterable.next())) {
|
|
216
|
+
if (aResult.done) {
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
var bIterable = b.values();
|
|
220
|
+
var hasMatch = false;
|
|
221
|
+
var matchIndex = 0;
|
|
222
|
+
while ((bResult = bIterable.next())) {
|
|
223
|
+
if (bResult.done) {
|
|
224
|
+
break;
|
|
222
225
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
isValueEqual = hasMatch;
|
|
234
|
-
});
|
|
226
|
+
if (!hasMatch &&
|
|
227
|
+
!matchedIndices[matchIndex] &&
|
|
228
|
+
(hasMatch = state.equals(aResult.value, bResult.value, aResult.value, bResult.value, a, b, state))) {
|
|
229
|
+
matchedIndices[matchIndex] = true;
|
|
230
|
+
}
|
|
231
|
+
matchIndex++;
|
|
232
|
+
}
|
|
233
|
+
if (!hasMatch) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
235
236
|
}
|
|
236
|
-
return
|
|
237
|
+
return true;
|
|
237
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* Whether the TypedArray instances are equal in value.
|
|
241
|
+
*/
|
|
238
242
|
function areTypedArraysEqual(a, b) {
|
|
239
243
|
var index = a.length;
|
|
240
244
|
if (b.length !== index) {
|
|
@@ -266,7 +270,7 @@ var getTag = Object.prototype.toString.call.bind(Object.prototype.toString);
|
|
|
266
270
|
/**
|
|
267
271
|
* Create a comparator method based on the type-specific equality comparators passed.
|
|
268
272
|
*/
|
|
269
|
-
function
|
|
273
|
+
function createEqualityComparator(_a) {
|
|
270
274
|
var areArraysEqual = _a.areArraysEqual, areDatesEqual = _a.areDatesEqual, areMapsEqual = _a.areMapsEqual, areObjectsEqual = _a.areObjectsEqual, arePrimitiveWrappersEqual = _a.arePrimitiveWrappersEqual, areRegExpsEqual = _a.areRegExpsEqual, areSetsEqual = _a.areSetsEqual, areTypedArraysEqual = _a.areTypedArraysEqual;
|
|
271
275
|
/**
|
|
272
276
|
* compare the value of the two objects and return true if they are equivalent in values
|
|
@@ -280,9 +284,13 @@ function createComparator(_a) {
|
|
|
280
284
|
// of them being equal but not strictly is if they are both `NaN`. Since
|
|
281
285
|
// `NaN` is uniquely not equal to itself, we can use self-comparison of
|
|
282
286
|
// both objects, which is faster than `isNaN()`.
|
|
283
|
-
if (
|
|
287
|
+
if (a == null ||
|
|
288
|
+
b == null ||
|
|
289
|
+
typeof a !== 'object' ||
|
|
290
|
+
typeof b !== 'object') {
|
|
284
291
|
return a !== a && b !== b;
|
|
285
292
|
}
|
|
293
|
+
var constructor = a.constructor;
|
|
286
294
|
// Checks are listed in order of commonality of use-case:
|
|
287
295
|
// 1. Common complex object types (plain object, array)
|
|
288
296
|
// 2. Common data values (date, regexp)
|
|
@@ -291,43 +299,47 @@ function createComparator(_a) {
|
|
|
291
299
|
// Inherently this is both subjective and assumptive, however
|
|
292
300
|
// when reviewing comparable libraries in the wild this order
|
|
293
301
|
// appears to be generally consistent.
|
|
302
|
+
// Constructors should match, otherwise there is potential for false positives
|
|
303
|
+
// between class and subclass or custom object and POJO.
|
|
304
|
+
if (constructor !== b.constructor) {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
294
307
|
// `isPlainObject` only checks against the object's own realm. Cross-realm
|
|
295
308
|
// comparisons are rare, and will be handled in the ultimate fallback, so
|
|
296
|
-
// we can avoid the
|
|
297
|
-
if (
|
|
309
|
+
// we can avoid capturing the string tag.
|
|
310
|
+
if (constructor === Object) {
|
|
298
311
|
return areObjectsEqual(a, b, state);
|
|
299
312
|
}
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
return false;
|
|
313
|
+
// `isArray()` works on subclasses and is cross-realm, so we can avoid capturing
|
|
314
|
+
// the string tag or doing an `instanceof` check.
|
|
315
|
+
if (isArray(a)) {
|
|
316
|
+
return areArraysEqual(a, b, state);
|
|
305
317
|
}
|
|
306
|
-
// `
|
|
307
|
-
// the
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
var bArray = isArray(b);
|
|
311
|
-
if (aArray || bArray) {
|
|
312
|
-
return aArray === bArray && areArraysEqual(a, b, state);
|
|
313
|
-
}
|
|
314
|
-
// Prioritize the `TypedArray` check because it does not require capturing the tag, which is a
|
|
315
|
-
// slower path.
|
|
316
|
-
if (isTypedArray) {
|
|
317
|
-
aArray = isTypedArray(a);
|
|
318
|
-
bArray = isTypedArray(b);
|
|
319
|
-
if (aArray || bArray) {
|
|
320
|
-
return aArray === bArray && areTypedArraysEqual(a, b, state);
|
|
321
|
-
}
|
|
318
|
+
// `isTypedArray()` works on all possible TypedArray classes, so we can avoid
|
|
319
|
+
// capturing the string tag or comparing against all possible constructors.
|
|
320
|
+
if (isTypedArray != null && isTypedArray(a)) {
|
|
321
|
+
return areTypedArraysEqual(a, b, state);
|
|
322
322
|
}
|
|
323
|
-
//
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
//
|
|
327
|
-
|
|
328
|
-
if (
|
|
329
|
-
return
|
|
323
|
+
// Try to fast-path equality checks for other complex object types in the
|
|
324
|
+
// same realm to avoid capturing the string tag. Strict equality is used
|
|
325
|
+
// instead of `instanceof` because it is more performant for the common
|
|
326
|
+
// use-case. If someone is subclassing a native class, it will be handled
|
|
327
|
+
// with the string tag comparison.
|
|
328
|
+
if (constructor === Date) {
|
|
329
|
+
return areDatesEqual(a, b, state);
|
|
330
|
+
}
|
|
331
|
+
if (constructor === RegExp) {
|
|
332
|
+
return areRegExpsEqual(a, b, state);
|
|
333
|
+
}
|
|
334
|
+
if (constructor === Map) {
|
|
335
|
+
return areMapsEqual(a, b, state);
|
|
330
336
|
}
|
|
337
|
+
if (constructor === Set) {
|
|
338
|
+
return areSetsEqual(a, b, state);
|
|
339
|
+
}
|
|
340
|
+
// Since this is a custom object, capture the string tag to determing its type.
|
|
341
|
+
// This is reasonably performant in modern environments like v8 and SpiderMonkey.
|
|
342
|
+
var tag = getTag(a);
|
|
331
343
|
if (tag === DATE_TAG) {
|
|
332
344
|
return areDatesEqual(a, b, state);
|
|
333
345
|
}
|
|
@@ -340,11 +352,9 @@ function createComparator(_a) {
|
|
|
340
352
|
if (tag === SET_TAG) {
|
|
341
353
|
return areSetsEqual(a, b, state);
|
|
342
354
|
}
|
|
343
|
-
// If a simple object tag, then we can prioritize a simple object comparison because
|
|
344
|
-
// it is likely a custom class.
|
|
345
355
|
if (tag === OBJECT_TAG) {
|
|
346
|
-
// The exception for value comparison is `Promise`-like
|
|
347
|
-
// treated the same as standard `Promise` objects, which means strict equality, and if
|
|
356
|
+
// The exception for value comparison is custom `Promise`-like class instances. These should
|
|
357
|
+
// be treated the same as standard `Promise` objects, which means strict equality, and if
|
|
348
358
|
// it reaches this point then that strict equality comparison has already failed.
|
|
349
359
|
return (typeof a.then !== 'function' &&
|
|
350
360
|
typeof b.then !== 'function' &&
|
|
@@ -377,7 +387,7 @@ function createComparator(_a) {
|
|
|
377
387
|
/**
|
|
378
388
|
* Create the configuration object used for building comparators.
|
|
379
389
|
*/
|
|
380
|
-
function
|
|
390
|
+
function createEqualityComparatorConfig(_a) {
|
|
381
391
|
var circular = _a.circular, createCustomConfig = _a.createCustomConfig, strict = _a.strict;
|
|
382
392
|
var config = {
|
|
383
393
|
areArraysEqual: strict
|
|
@@ -396,7 +406,7 @@ function createComparatorConfig(_a) {
|
|
|
396
406
|
? combineComparators(areSetsEqual, areObjectsEqualStrict)
|
|
397
407
|
: areSetsEqual,
|
|
398
408
|
areTypedArraysEqual: strict
|
|
399
|
-
?
|
|
409
|
+
? areObjectsEqualStrict
|
|
400
410
|
: areTypedArraysEqual,
|
|
401
411
|
};
|
|
402
412
|
if (createCustomConfig) {
|
|
@@ -416,6 +426,51 @@ function createComparatorConfig(_a) {
|
|
|
416
426
|
}
|
|
417
427
|
return config;
|
|
418
428
|
}
|
|
429
|
+
/**
|
|
430
|
+
* Default equality comparator pass-through, used as the standard `isEqual` creator for
|
|
431
|
+
* use inside the built comparator.
|
|
432
|
+
*/
|
|
433
|
+
function createInternalEqualityComparator(compare) {
|
|
434
|
+
return function (a, b, _indexOrKeyA, _indexOrKeyB, _parentA, _parentB, state) {
|
|
435
|
+
return compare(a, b, state);
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Create the `isEqual` function used by the consuming application.
|
|
440
|
+
*/
|
|
441
|
+
function createIsEqual(_a) {
|
|
442
|
+
var circular = _a.circular, comparator = _a.comparator, createState = _a.createState, equals = _a.equals, strict = _a.strict;
|
|
443
|
+
if (createState) {
|
|
444
|
+
return function isEqual(a, b) {
|
|
445
|
+
var _a = createState(), _b = _a.cache, cache = _b === void 0 ? circular ? new WeakMap() : undefined : _b, meta = _a.meta;
|
|
446
|
+
return comparator(a, b, {
|
|
447
|
+
cache: cache,
|
|
448
|
+
equals: equals,
|
|
449
|
+
meta: meta,
|
|
450
|
+
strict: strict,
|
|
451
|
+
});
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
if (circular) {
|
|
455
|
+
return function isEqual(a, b) {
|
|
456
|
+
return comparator(a, b, {
|
|
457
|
+
cache: new WeakMap(),
|
|
458
|
+
equals: equals,
|
|
459
|
+
meta: undefined,
|
|
460
|
+
strict: strict,
|
|
461
|
+
});
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
var state = {
|
|
465
|
+
cache: undefined,
|
|
466
|
+
equals: equals,
|
|
467
|
+
meta: undefined,
|
|
468
|
+
strict: strict,
|
|
469
|
+
};
|
|
470
|
+
return function isEqual(a, b) {
|
|
471
|
+
return comparator(a, b, state);
|
|
472
|
+
};
|
|
473
|
+
}
|
|
419
474
|
|
|
420
475
|
/**
|
|
421
476
|
* Whether the items passed are deeply-equal in value.
|
|
@@ -441,29 +496,29 @@ var strictCircularDeepEqual = createCustomEqual({
|
|
|
441
496
|
* Whether the items passed are shallowly-equal in value.
|
|
442
497
|
*/
|
|
443
498
|
var shallowEqual = createCustomEqual({
|
|
444
|
-
|
|
499
|
+
createInternalComparator: function () { return sameValueZeroEqual; },
|
|
445
500
|
});
|
|
446
501
|
/**
|
|
447
502
|
* Whether the items passed are shallowly-equal in value based on strict comparison
|
|
448
503
|
*/
|
|
449
504
|
var strictShallowEqual = createCustomEqual({
|
|
450
|
-
comparator: sameValueZeroEqual,
|
|
451
505
|
strict: true,
|
|
506
|
+
createInternalComparator: function () { return sameValueZeroEqual; },
|
|
452
507
|
});
|
|
453
508
|
/**
|
|
454
509
|
* Whether the items passed are shallowly-equal in value, including circular references.
|
|
455
510
|
*/
|
|
456
511
|
var circularShallowEqual = createCustomEqual({
|
|
457
|
-
comparator: sameValueZeroEqual,
|
|
458
512
|
circular: true,
|
|
513
|
+
createInternalComparator: function () { return sameValueZeroEqual; },
|
|
459
514
|
});
|
|
460
515
|
/**
|
|
461
516
|
* Whether the items passed are shallowly-equal in value, including circular references,
|
|
462
517
|
* based on strict comparison.
|
|
463
518
|
*/
|
|
464
519
|
var strictCircularShallowEqual = createCustomEqual({
|
|
465
|
-
comparator: sameValueZeroEqual,
|
|
466
520
|
circular: true,
|
|
521
|
+
createInternalComparator: function () { return sameValueZeroEqual; },
|
|
467
522
|
strict: true,
|
|
468
523
|
});
|
|
469
524
|
/**
|
|
@@ -476,43 +531,13 @@ var strictCircularShallowEqual = createCustomEqual({
|
|
|
476
531
|
*/
|
|
477
532
|
function createCustomEqual(options) {
|
|
478
533
|
if (options === void 0) { options = {}; }
|
|
479
|
-
var
|
|
480
|
-
var config =
|
|
481
|
-
var
|
|
482
|
-
var
|
|
483
|
-
(
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
if (createState) {
|
|
487
|
-
return function isEqual(a, b, metaOverride) {
|
|
488
|
-
var _a = createState(isEqualCustom), _b = _a.cache, cache = _b === void 0 ? circular ? new WeakMap() : undefined : _b, _c = _a.equals, equals = _c === void 0 ? isEqualCustomComparator : _c, meta = _a.meta, _d = _a.strict, strict = _d === void 0 ? baseStrict : _d;
|
|
489
|
-
return isEqualCustom(a, b, {
|
|
490
|
-
cache: cache,
|
|
491
|
-
equals: equals,
|
|
492
|
-
meta: metaOverride !== undefined ? metaOverride : meta,
|
|
493
|
-
strict: strict,
|
|
494
|
-
});
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
if (circular) {
|
|
498
|
-
return function equals(a, b) {
|
|
499
|
-
return isEqualCustom(a, b, {
|
|
500
|
-
cache: new WeakMap(),
|
|
501
|
-
equals: isEqualCustomComparator,
|
|
502
|
-
meta: undefined,
|
|
503
|
-
strict: baseStrict,
|
|
504
|
-
});
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
var state = Object.freeze({
|
|
508
|
-
cache: undefined,
|
|
509
|
-
equals: isEqualCustomComparator,
|
|
510
|
-
meta: undefined,
|
|
511
|
-
strict: baseStrict,
|
|
512
|
-
});
|
|
513
|
-
return function equals(a, b) {
|
|
514
|
-
return isEqualCustom(a, b, state);
|
|
515
|
-
};
|
|
534
|
+
var _a = options.circular, circular = _a === void 0 ? false : _a, createCustomInternalComparator = options.createInternalComparator, createState = options.createState, _b = options.strict, strict = _b === void 0 ? false : _b;
|
|
535
|
+
var config = createEqualityComparatorConfig(options);
|
|
536
|
+
var comparator = createEqualityComparator(config);
|
|
537
|
+
var equals = createCustomInternalComparator
|
|
538
|
+
? createCustomInternalComparator(comparator)
|
|
539
|
+
: createInternalEqualityComparator(comparator);
|
|
540
|
+
return createIsEqual({ circular: circular, comparator: comparator, createState: createState, equals: equals, strict: strict });
|
|
516
541
|
}
|
|
517
542
|
|
|
518
543
|
exports.circularDeepEqual = circularDeepEqual;
|