sodiray / radash

Functional utility library - modern, simple, typed, powerful
https://radash-docs.vercel.app
MIT License
4.18k stars 167 forks source link

`isEqual` does not compare Maps correctly #325

Open Blafasel3 opened 1 year ago

Blafasel3 commented 1 year ago

This test case fails: expect(isEqual(new Map(), new Map([['1', '123']]))).toBeFalsy(); where as expect(isEqual({}, {'1': '123'})).toBeFalsy(); succeeds. The issue is that Reflect.ownKeys(<map>) returns an empty array. and so the keys are never compared.

Same holds for expect(isEqual(new Set(), new Set([1, 2]))).toBeFalsy(); Version: "^10.8.1", also occurs in version 11.

Blafasel3 commented 1 year ago

One solution for Maps could be:

  if (a instanceof Map && b instanceof Map) {
    return isEqual(Object.fromEntries(a), Object.fromEntries(b));
  }
jwatzman commented 1 year ago

The same thing looks to happen with Set, I think for the same reason.

thomasjahoda commented 4 months ago

I patched isEqual to support Maps and Sets on any level of objects. The workaround by @Blafasel3 is only for top-level maps and is sensitive to insertion order. WARNING: My patch is insensitive to insertion order of the Map and Set entries. You could also define Maps to only be equal if the insertion order of the entries is also equal.

diff --git a/node_modules/radash/dist/cjs/typed.cjs b/node_modules/radash/dist/cjs/typed.cjs
index e3868b5..b8a2aad 100644
--- a/node_modules/radash/dist/cjs/typed.cjs
+++ b/node_modules/radash/dist/cjs/typed.cjs
@@ -72,6 +72,35 @@ const isEqual = (x, y) => {
   if (x instanceof RegExp && y instanceof RegExp) {
     return x.toString() === y.toString();
   }
+  // patched because of https://github.com/rayepps/radash/issues/325
+  if (x instanceof Map) {
+    if (!(y instanceof Map)) {
+      return false;
+    }
+    if (x.size !== y.size) {
+      return false;
+    }
+    for (const [key, value] of x.entries()) {
+      if (!y.has(key) || !isEqual(value, y.get(key))) {
+        return false;
+      }
+    }
+    return true;
+  }
+  if (x instanceof Set) {
+    if (!(y instanceof Set)) {
+      return false;
+    }
+    if (x.size !== y.size) {
+      return false;
+    }
+    for (const value of x) {
+      if (!y.has(value)) {
+        return false;
+      }
+    }
+    return true;
+  }
   if (typeof x !== "object" || x === null || typeof y !== "object" || y === null) {
     return false;
   }
diff --git a/node_modules/radash/dist/esm/typed.mjs b/node_modules/radash/dist/esm/typed.mjs
index d64b1ab..21479c7 100644
--- a/node_modules/radash/dist/esm/typed.mjs
+++ b/node_modules/radash/dist/esm/typed.mjs
@@ -70,6 +70,35 @@ const isEqual = (x, y) => {
   if (x instanceof RegExp && y instanceof RegExp) {
     return x.toString() === y.toString();
   }
+  // patched because of https://github.com/rayepps/radash/issues/325
+  if (x instanceof Map) {
+    if (!(y instanceof Map)) {
+      return false;
+    }
+    if (x.size !== y.size) {
+      return false;
+    }
+    for (const [key, value] of x.entries()) {
+      if (!y.has(key) || !isEqual(value, y.get(key))) {
+        return false;
+      }
+    }
+    return true;
+  }
+  if (x instanceof Set) {
+    if (!(y instanceof Set)) {
+      return false;
+    }
+    if (x.size !== y.size) {
+      return false;
+    }
+    for (const value of x) {
+      if (!y.has(value)) {
+        return false;
+      }
+    }
+    return true;
+  }
   if (typeof x !== "object" || x === null || typeof y !== "object" || y === null) {
     return false;
   }