fast-is-equal 1.0.2 → 1.0.4
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 +4 -2
- package/dist/__tests__/fastIsEqual.test.js +154 -1
- package/dist/index.js +21 -3
- package/package.json +16 -2
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# fast-is-equal
|
|
2
2
|
|
|
3
|
-
Blazing-fast equality checks, minus the baggage. A lean, standalone alternative to Lodash’s `isEqual
|
|
3
|
+
Blazing-fast equality checks, minus the baggage. A lean, standalone alternative to Lodash’s `isEqual` - because speed matters.
|
|
4
|
+
|
|
5
|
+
[](https://badge.fury.io/js/fast-is-equal)
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
@@ -174,4 +176,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
174
176
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
175
177
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
176
178
|
SOFTWARE.
|
|
177
|
-
```
|
|
179
|
+
```
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const index_1 = require("../index");
|
|
4
|
-
describe('
|
|
4
|
+
describe('fastIsEqual', () => {
|
|
5
5
|
// **Primitives**
|
|
6
6
|
it('should return true for identical primitives', () => {
|
|
7
7
|
expect((0, index_1.fastIsEqual)(1, 1)).toBe(true);
|
|
@@ -37,6 +37,21 @@ describe('isEqual', () => {
|
|
|
37
37
|
const obj2 = { a: 1, b: { c: 2 } };
|
|
38
38
|
expect((0, index_1.fastIsEqual)(obj1, obj2)).toBe(true);
|
|
39
39
|
});
|
|
40
|
+
it('should return true for deeply unequal objects', () => {
|
|
41
|
+
const obj1 = { a: 1, b: { c: 2 } };
|
|
42
|
+
const obj2 = { a: 1, b: { c: 3 } };
|
|
43
|
+
expect((0, index_1.fastIsEqual)(obj1, obj2)).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
it('should return true for deeply equal objects with array', () => {
|
|
46
|
+
const obj1 = { a: 1, b: { c: [1, 2, 3] } };
|
|
47
|
+
const obj2 = { a: 1, b: { c: [1, 2, 3] } };
|
|
48
|
+
expect((0, index_1.fastIsEqual)(obj1, obj2)).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
it('should return true for deeply unequal objects with array', () => {
|
|
51
|
+
const obj1 = { a: 1, b: { c: [1, 2, 3] } };
|
|
52
|
+
const obj2 = { a: 1, b: { c: [1, 2, 3, 4] } };
|
|
53
|
+
expect((0, index_1.fastIsEqual)(obj1, obj2)).toBe(false);
|
|
54
|
+
});
|
|
40
55
|
it('should return false for objects with different keys', () => {
|
|
41
56
|
const obj1 = { a: 1 };
|
|
42
57
|
const obj2 = { b: 1 };
|
|
@@ -67,6 +82,11 @@ describe('isEqual', () => {
|
|
|
67
82
|
const arr2 = [1, 3];
|
|
68
83
|
expect((0, index_1.fastIsEqual)(arr1, arr2)).toBe(false);
|
|
69
84
|
});
|
|
85
|
+
it('should return true for arrays with different order', () => {
|
|
86
|
+
const arr1 = [1, 2, 3, 4];
|
|
87
|
+
const arr2 = [1, 2, 4, 3];
|
|
88
|
+
expect((0, index_1.fastIsEqual)(arr1, arr2)).toBe(false);
|
|
89
|
+
});
|
|
70
90
|
// **Dates**
|
|
71
91
|
it('should return true for identical dates', () => {
|
|
72
92
|
const date = new Date();
|
|
@@ -177,4 +197,137 @@ describe('isEqual', () => {
|
|
|
177
197
|
expect((0, index_1.fastIsEqual)({}, [])).toBe(false);
|
|
178
198
|
expect((0, index_1.fastIsEqual)(new Map(), new Set())).toBe(false);
|
|
179
199
|
});
|
|
200
|
+
// **Symbol Tests**
|
|
201
|
+
it('should return true for identical symbols', () => {
|
|
202
|
+
const sym = Symbol('test');
|
|
203
|
+
expect((0, index_1.fastIsEqual)(sym, sym)).toBe(true);
|
|
204
|
+
});
|
|
205
|
+
it('should return false for different symbols with same description', () => {
|
|
206
|
+
expect((0, index_1.fastIsEqual)(Symbol('test'), Symbol('test'))).toBe(false);
|
|
207
|
+
});
|
|
208
|
+
// **BigInt Tests**
|
|
209
|
+
it('should return true for identical BigInts', () => {
|
|
210
|
+
expect((0, index_1.fastIsEqual)(BigInt(123), BigInt(123))).toBe(true);
|
|
211
|
+
});
|
|
212
|
+
it('should return false for different BigInts', () => {
|
|
213
|
+
expect((0, index_1.fastIsEqual)(BigInt(123), BigInt(456))).toBe(false);
|
|
214
|
+
});
|
|
215
|
+
// **Nested Complex Structures**
|
|
216
|
+
it('should handle deeply nested objects with arrays, maps and sets', () => {
|
|
217
|
+
const obj1 = {
|
|
218
|
+
a: [1, 2, { b: new Map([['key', new Set([1, 2, { c: 3 }])]]) }],
|
|
219
|
+
d: new Set([new Map([['e', { f: 7 }]])])
|
|
220
|
+
};
|
|
221
|
+
const obj2 = {
|
|
222
|
+
a: [1, 2, { b: new Map([['key', new Set([1, 2, { c: 3 }])]]) }],
|
|
223
|
+
d: new Set([new Map([['e', { f: 7 }]])])
|
|
224
|
+
};
|
|
225
|
+
expect((0, index_1.fastIsEqual)(obj1, obj2)).toBe(true);
|
|
226
|
+
});
|
|
227
|
+
it('should detect differences in deeply nested structures', () => {
|
|
228
|
+
const obj1 = {
|
|
229
|
+
a: [1, 2, { b: new Map([['key', new Set([1, 2, { c: 3 }])]]) }]
|
|
230
|
+
};
|
|
231
|
+
const obj2 = {
|
|
232
|
+
a: [1, 2, { b: new Map([['key', new Set([1, 2, { c: 4 }])]]) }]
|
|
233
|
+
};
|
|
234
|
+
expect((0, index_1.fastIsEqual)(obj1, obj2)).toBe(false);
|
|
235
|
+
});
|
|
236
|
+
// **Typed Arrays**
|
|
237
|
+
it('should compare typed arrays correctly', () => {
|
|
238
|
+
expect((0, index_1.fastIsEqual)(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2, 3]))).toBe(true);
|
|
239
|
+
expect((0, index_1.fastIsEqual)(new Int32Array([1, 2, 3]), new Int32Array([1, 2, 3]))).toBe(true);
|
|
240
|
+
expect((0, index_1.fastIsEqual)(new Float64Array([1.1, 2.2]), new Float64Array([1.1, 2.2]))).toBe(true);
|
|
241
|
+
expect((0, index_1.fastIsEqual)(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2, 4]))).toBe(false);
|
|
242
|
+
expect((0, index_1.fastIsEqual)(new Uint8Array([1, 2]), new Int8Array([1, 2]))).toBe(false);
|
|
243
|
+
});
|
|
244
|
+
// **Partial Object Comparison with Extra Keys**
|
|
245
|
+
it('should detect when objects have extra keys', () => {
|
|
246
|
+
const obj1 = { a: 1, b: 2 };
|
|
247
|
+
const obj2 = { a: 1, b: 2, c: 3 };
|
|
248
|
+
expect((0, index_1.fastIsEqual)(obj1, obj2)).toBe(false);
|
|
249
|
+
});
|
|
250
|
+
// **Object with Different Property Orders**
|
|
251
|
+
it('should return true for objects with same properties in different order', () => {
|
|
252
|
+
const obj1 = { a: 1, b: 2, c: 3 };
|
|
253
|
+
const obj2 = { c: 3, a: 1, b: 2 };
|
|
254
|
+
expect((0, index_1.fastIsEqual)(obj1, obj2)).toBe(true);
|
|
255
|
+
});
|
|
256
|
+
// **Map/Set with Object Keys/Values**
|
|
257
|
+
it('should handle Maps with object keys correctly', () => {
|
|
258
|
+
const key1 = { id: 1 };
|
|
259
|
+
const key2 = { id: 1 };
|
|
260
|
+
const map1 = new Map([[key1, 'value']]);
|
|
261
|
+
const map2 = new Map([[key2, 'value']]);
|
|
262
|
+
// Objects as keys are compared by reference
|
|
263
|
+
expect((0, index_1.fastIsEqual)(map1, map2)).toBe(false);
|
|
264
|
+
// Same reference should work
|
|
265
|
+
const map3 = new Map([[key1, 'value']]);
|
|
266
|
+
expect((0, index_1.fastIsEqual)(map1, map3)).toBe(true);
|
|
267
|
+
});
|
|
268
|
+
// **WeakMap and WeakSet** (if supported by fastIsEqual)
|
|
269
|
+
it('should return true for identical WeakMap/WeakSet references', () => {
|
|
270
|
+
const weakMap = new WeakMap();
|
|
271
|
+
const weakSet = new WeakSet();
|
|
272
|
+
expect((0, index_1.fastIsEqual)(weakMap, weakMap)).toBe(true);
|
|
273
|
+
expect((0, index_1.fastIsEqual)(weakSet, weakSet)).toBe(true);
|
|
274
|
+
});
|
|
275
|
+
it('should return false for different WeakMap/WeakSet instances', () => {
|
|
276
|
+
expect((0, index_1.fastIsEqual)(new WeakMap(), new WeakMap())).toBe(false);
|
|
277
|
+
expect((0, index_1.fastIsEqual)(new WeakSet(), new WeakSet())).toBe(false);
|
|
278
|
+
});
|
|
279
|
+
// **Cross-circular References**
|
|
280
|
+
it('should handle cross-circular references', () => {
|
|
281
|
+
const obj1 = { name: 'object1' };
|
|
282
|
+
const obj2 = { name: 'object2' };
|
|
283
|
+
obj1.ref = obj2;
|
|
284
|
+
obj2.ref = obj1;
|
|
285
|
+
const obj3 = { name: 'object1' };
|
|
286
|
+
const obj4 = { name: 'object2' };
|
|
287
|
+
obj3.ref = obj4;
|
|
288
|
+
obj4.ref = obj3;
|
|
289
|
+
expect((0, index_1.fastIsEqual)(obj1, obj3)).toBe(true);
|
|
290
|
+
});
|
|
291
|
+
it('should detect differences in cross-circular references', () => {
|
|
292
|
+
const obj1 = { name: 'object1' };
|
|
293
|
+
const obj2 = { name: 'object2' };
|
|
294
|
+
obj1.ref = obj2;
|
|
295
|
+
obj2.ref = obj1;
|
|
296
|
+
const obj3 = { name: 'object1' };
|
|
297
|
+
const obj4 = { name: 'differentName' };
|
|
298
|
+
obj3.ref = obj4;
|
|
299
|
+
obj4.ref = obj3;
|
|
300
|
+
expect((0, index_1.fastIsEqual)(obj1, obj3)).toBe(false);
|
|
301
|
+
});
|
|
302
|
+
// **Edge Cases**
|
|
303
|
+
it('should handle -0 and +0 comparison', () => {
|
|
304
|
+
expect((0, index_1.fastIsEqual)(0, -0)).toBe(true); // Depending on implementation
|
|
305
|
+
});
|
|
306
|
+
it('should handle Object.create(null)', () => {
|
|
307
|
+
const obj1 = Object.create(null);
|
|
308
|
+
obj1.a = 1;
|
|
309
|
+
const obj2 = Object.create(null);
|
|
310
|
+
obj2.a = 1;
|
|
311
|
+
expect((0, index_1.fastIsEqual)(obj1, obj2)).toBe(true);
|
|
312
|
+
});
|
|
313
|
+
// **Performance Stress Test**
|
|
314
|
+
it('should handle large objects without stack overflow', () => {
|
|
315
|
+
// Create a very deep object
|
|
316
|
+
let obj1 = {};
|
|
317
|
+
let obj2 = {};
|
|
318
|
+
let temp1 = obj1;
|
|
319
|
+
let temp2 = obj2;
|
|
320
|
+
for (let i = 0; i < 1000; i++) {
|
|
321
|
+
temp1.next = {};
|
|
322
|
+
temp2.next = {};
|
|
323
|
+
temp1 = temp1.next;
|
|
324
|
+
temp2 = temp2.next;
|
|
325
|
+
temp1.value = i;
|
|
326
|
+
temp2.value = i;
|
|
327
|
+
}
|
|
328
|
+
expect((0, index_1.fastIsEqual)(obj1, obj2)).toBe(true);
|
|
329
|
+
// Change a value deep in the structure
|
|
330
|
+
temp2.value = 'different';
|
|
331
|
+
expect((0, index_1.fastIsEqual)(obj1, obj2)).toBe(false);
|
|
332
|
+
});
|
|
180
333
|
});
|
package/dist/index.js
CHANGED
|
@@ -52,6 +52,13 @@ function deepEqual(valA, valB, visited) {
|
|
|
52
52
|
return valA === valB;
|
|
53
53
|
if (typeof valA !== typeof valB)
|
|
54
54
|
return false;
|
|
55
|
+
// Handle WeakMap and WeakSet (reference equality)
|
|
56
|
+
if (valA instanceof WeakMap && valB instanceof WeakMap) {
|
|
57
|
+
return valA === valB;
|
|
58
|
+
}
|
|
59
|
+
if (valA instanceof WeakSet && valB instanceof WeakSet) {
|
|
60
|
+
return valA === valB;
|
|
61
|
+
}
|
|
55
62
|
// Handle Dates
|
|
56
63
|
if (valA instanceof Date && valB instanceof Date) {
|
|
57
64
|
return valA.getTime() === valB.getTime();
|
|
@@ -96,12 +103,23 @@ function deepEqual(valA, valB, visited) {
|
|
|
96
103
|
}
|
|
97
104
|
return true;
|
|
98
105
|
}
|
|
99
|
-
// Handle Sets
|
|
106
|
+
// Handle Sets with deep equality
|
|
100
107
|
if (valA instanceof Set && valB instanceof Set) {
|
|
101
108
|
if (valA.size !== valB.size)
|
|
102
109
|
return false;
|
|
103
|
-
|
|
104
|
-
|
|
110
|
+
const arrA = Array.from(valA);
|
|
111
|
+
const arrB = Array.from(valB);
|
|
112
|
+
const matched = new Set();
|
|
113
|
+
for (const elemA of arrA) {
|
|
114
|
+
let found = false;
|
|
115
|
+
for (const elemB of arrB) {
|
|
116
|
+
if (!matched.has(elemB) && deepEqual(elemA, elemB, visited)) {
|
|
117
|
+
matched.add(elemB);
|
|
118
|
+
found = true;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (!found)
|
|
105
123
|
return false;
|
|
106
124
|
}
|
|
107
125
|
return true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fast-is-equal",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Blazing-fast equality checks, minus the baggage. A lean, standalone alternative to Lodash's isEqual—because speed matters.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lodash",
|
|
@@ -40,5 +40,19 @@
|
|
|
40
40
|
"ts-node": "^10.9.2",
|
|
41
41
|
"tslib": "^2.8.1",
|
|
42
42
|
"typescript": "^5.8.2"
|
|
43
|
-
}
|
|
43
|
+
},
|
|
44
|
+
"funding": [
|
|
45
|
+
{
|
|
46
|
+
"type": "individual",
|
|
47
|
+
"url": "https://www.paypal.com/paypalme/jairajjangle001/usd"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"type": "individual",
|
|
51
|
+
"url": "https://liberapay.com/FutureJJ/donate"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"type": "individual",
|
|
55
|
+
"url": "https://ko-fi.com/futurejj"
|
|
56
|
+
}
|
|
57
|
+
]
|
|
44
58
|
}
|