radash icon indicating copy to clipboard operation
radash copied to clipboard

`isEqual` does not compare Maps correctly

Open Blafasel3 opened this issue 2 years ago • 3 comments

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 avatar Jul 05 '23 13:07 Blafasel3

One solution for Maps could be:

  if (a instanceof Map && b instanceof Map) {
    return isEqual(Object.fromEntries(a), Object.fromEntries(b));
  }

Blafasel3 avatar Jul 05 '23 14:07 Blafasel3

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

jwatzman avatar Sep 05 '23 10:09 jwatzman

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;
   }

thomasjahoda avatar Apr 21 '24 11:04 thomasjahoda

@thomasjahoda not sure if this is maintained anymore, but maybe open a PR?

Blafasel3 avatar Sep 25 '24 12:09 Blafasel3

The radashi fork is accepting PRs for this.

aleclarson avatar Oct 14 '24 15:10 aleclarson

Added testcases in this inside radashi. It was already working. Closing this and switching over.

Blafasel3 avatar Oct 22 '24 10:10 Blafasel3