@longlast/equals 0.4.3 → 0.5.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/README.md +6 -0
- package/dist/index.d.ts +33 -2
- package/dist/index.js +49 -5
- package/package.json +3 -2
package/README.md
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -18,15 +18,46 @@ import { type Curried2 } from "@longlast/curry";
|
|
|
18
18
|
* elements are equal (according to `equals`).
|
|
19
19
|
* - Sets are equal iff they contain the same elements. Note that set
|
|
20
20
|
* elements are _not_ deeply compared.
|
|
21
|
+
* - Partially applied curried functions are equal iff they originate from
|
|
22
|
+
* the same curried function and their bound arguments are equal
|
|
23
|
+
* according to `equals`. See {@link curry}.
|
|
21
24
|
* - Other objects are equal iff they have the same prototype (e.g. the same
|
|
22
25
|
* class) and the same set of enumerable string-keyed properties, and the
|
|
23
26
|
* values of their corresponding properties are equal (according to
|
|
24
27
|
* `equals`).
|
|
25
28
|
*
|
|
29
|
+
* You can customize how `equals()` compares values of a specific class by
|
|
30
|
+
* using the {@link symbols.$equals $equals} symbol to define a method on that class. For
|
|
31
|
+
* example:
|
|
32
|
+
*
|
|
33
|
+
* ```ts
|
|
34
|
+
* import {$equals} from "@longlast/symbols"
|
|
35
|
+
*
|
|
36
|
+
* class HttpError extends Error {
|
|
37
|
+
* private statusCode: number;
|
|
38
|
+
* constructor(message: string, statusCode: number) {
|
|
39
|
+
* super(message);
|
|
40
|
+
* this.statusCode = statusCode;
|
|
41
|
+
* }
|
|
42
|
+
*
|
|
43
|
+
* [$equals](other: unknown) {
|
|
44
|
+
* return other instanceof HttpError &&
|
|
45
|
+
* other.statusCode === this.statusCode &&
|
|
46
|
+
* other.message === this.message;
|
|
47
|
+
* }
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* Note that this makes the comparison asymmetrical: `a` is considered equal to
|
|
52
|
+
* `b` iff `a[$equals](b)` returns truthy. The `$equals` method will always be
|
|
53
|
+
* called on the *first* argument to `equals()`.
|
|
54
|
+
*
|
|
55
|
+
* `equals()` is curried. See {@link curry}.
|
|
56
|
+
*
|
|
57
|
+
* ## Limitations
|
|
58
|
+
*
|
|
26
59
|
* `equals()` can throw a `RangeError` if one of its arguments contains a
|
|
27
60
|
* reference cycle. Avoid passing mutable objects to `equals()` unless you know
|
|
28
61
|
* that they do not contain cycles.
|
|
29
|
-
*
|
|
30
|
-
* `equals()` is curried.
|
|
31
62
|
*/
|
|
32
63
|
export declare const equals: Curried2<unknown, unknown, boolean>;
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* @module equals
|
|
3
3
|
*/
|
|
4
4
|
import { curry } from "@longlast/curry";
|
|
5
|
+
import { $boundArguments, $equals, $unapplied } from "@longlast/symbols";
|
|
5
6
|
/**
|
|
6
7
|
* @function
|
|
7
8
|
* Deeply compares two values, returning true if they're equal and false
|
|
@@ -18,16 +19,47 @@ import { curry } from "@longlast/curry";
|
|
|
18
19
|
* elements are equal (according to `equals`).
|
|
19
20
|
* - Sets are equal iff they contain the same elements. Note that set
|
|
20
21
|
* elements are _not_ deeply compared.
|
|
22
|
+
* - Partially applied curried functions are equal iff they originate from
|
|
23
|
+
* the same curried function and their bound arguments are equal
|
|
24
|
+
* according to `equals`. See {@link curry}.
|
|
21
25
|
* - Other objects are equal iff they have the same prototype (e.g. the same
|
|
22
26
|
* class) and the same set of enumerable string-keyed properties, and the
|
|
23
27
|
* values of their corresponding properties are equal (according to
|
|
24
28
|
* `equals`).
|
|
25
29
|
*
|
|
30
|
+
* You can customize how `equals()` compares values of a specific class by
|
|
31
|
+
* using the {@link symbols.$equals $equals} symbol to define a method on that class. For
|
|
32
|
+
* example:
|
|
33
|
+
*
|
|
34
|
+
* ```ts
|
|
35
|
+
* import {$equals} from "@longlast/symbols"
|
|
36
|
+
*
|
|
37
|
+
* class HttpError extends Error {
|
|
38
|
+
* private statusCode: number;
|
|
39
|
+
* constructor(message: string, statusCode: number) {
|
|
40
|
+
* super(message);
|
|
41
|
+
* this.statusCode = statusCode;
|
|
42
|
+
* }
|
|
43
|
+
*
|
|
44
|
+
* [$equals](other: unknown) {
|
|
45
|
+
* return other instanceof HttpError &&
|
|
46
|
+
* other.statusCode === this.statusCode &&
|
|
47
|
+
* other.message === this.message;
|
|
48
|
+
* }
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* Note that this makes the comparison asymmetrical: `a` is considered equal to
|
|
53
|
+
* `b` iff `a[$equals](b)` returns truthy. The `$equals` method will always be
|
|
54
|
+
* called on the *first* argument to `equals()`.
|
|
55
|
+
*
|
|
56
|
+
* `equals()` is curried. See {@link curry}.
|
|
57
|
+
*
|
|
58
|
+
* ## Limitations
|
|
59
|
+
*
|
|
26
60
|
* `equals()` can throw a `RangeError` if one of its arguments contains a
|
|
27
61
|
* reference cycle. Avoid passing mutable objects to `equals()` unless you know
|
|
28
62
|
* that they do not contain cycles.
|
|
29
|
-
*
|
|
30
|
-
* `equals()` is curried.
|
|
31
63
|
*/
|
|
32
64
|
export const equals = curry(_equals);
|
|
33
65
|
function _equals(a, b) {
|
|
@@ -36,6 +68,9 @@ function _equals(a, b) {
|
|
|
36
68
|
if (Object.is(a, b)) {
|
|
37
69
|
return true;
|
|
38
70
|
}
|
|
71
|
+
if (a != null && typeof a[$equals] === "function") {
|
|
72
|
+
return Boolean(a[$equals](b));
|
|
73
|
+
}
|
|
39
74
|
if (a instanceof Date && b instanceof Date) {
|
|
40
75
|
return Object.is(+a, +b);
|
|
41
76
|
}
|
|
@@ -46,11 +81,20 @@ function _equals(a, b) {
|
|
|
46
81
|
return (a.message === b.message &&
|
|
47
82
|
Object.getPrototypeOf(a) === Object.getPrototypeOf(b));
|
|
48
83
|
}
|
|
84
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
85
|
+
return a.length === b.length && a.every((_, i) => _equals(a[i], b[i]));
|
|
86
|
+
}
|
|
49
87
|
if (a instanceof Set && b instanceof Set) {
|
|
50
88
|
return a.size === b.size && [...a].every((v) => b.has(v));
|
|
51
89
|
}
|
|
52
|
-
if (
|
|
53
|
-
|
|
90
|
+
if (typeof a === "function" && typeof b === "function") {
|
|
91
|
+
const aArgs = a[$boundArguments];
|
|
92
|
+
const bArgs = b[$boundArguments];
|
|
93
|
+
const aUnapplied = a[$unapplied];
|
|
94
|
+
const bUnapplied = b[$unapplied];
|
|
95
|
+
return (aUnapplied != null &&
|
|
96
|
+
aUnapplied === bUnapplied &&
|
|
97
|
+
_equals(aArgs, bArgs));
|
|
54
98
|
}
|
|
55
99
|
if (a && b && typeof a === "object" && protoOf(a) === protoOf(b)) {
|
|
56
100
|
const bKeys = new Set(Object.keys(b));
|
|
@@ -58,7 +102,7 @@ function _equals(a, b) {
|
|
|
58
102
|
if (!bKeys.has(key)) {
|
|
59
103
|
return false;
|
|
60
104
|
}
|
|
61
|
-
if (!
|
|
105
|
+
if (!_equals(a[key], b[key])) {
|
|
62
106
|
return false;
|
|
63
107
|
}
|
|
64
108
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longlast/equals",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Deeply compares objects",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ben Christel (https://benchristel.com/)",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"#@longlast/equals": "./src/index.ts"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@longlast/curry": "^0.
|
|
19
|
+
"@longlast/curry": "^0.4.0",
|
|
20
|
+
"@longlast/symbols": "^1.0.0"
|
|
20
21
|
}
|
|
21
22
|
}
|