Repository: remeda/remeda
Pull request #637 (2024-04-13 21:21:22)

feat(isDeepEqual): add support for maps 
Add support for maps for the isDeepEqual function

Fixes: #626 

codesandbox-ci[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/remeda/remeda/pr/637/builds/494458) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit b81f16b28e3e13b6367a60b5f1d2d97393210e33:

|Sandbox| Source |
|--|--|
|[remeda-example-vanilla](https://codesandbox.io/s/remeda-example-vanilla-xymn38)| Configuration |

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/remeda/remeda/pull/637?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=remeda) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 98.59%. Comparing base [(`96a4e82`)](https://app.codecov.io/gh/remeda/remeda/commit/96a4e8276231ba124b6ff3ac6ff997112a56d2d6?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=remeda) to head [(`f908524`)](https://app.codecov.io/gh/remeda/remeda/pull/637?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=remeda).
> Report is 86 commits behind head on master.


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #637      +/-   ##
==========================================
+ Coverage   98.41%   98.59%   +0.17%     
==========================================
  Files         127      154      +27     
  Lines        7954    10961    +3007     
  Branches      724      904     +180     
==========================================
+ Hits         7828    10807    +2979     
- Misses        123      151      +28     
  Partials        3        3              
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/remeda/remeda/pull/637?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=remeda).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=remeda).


Patch:
diff --git a/eslint.config.mjs b/eslint.config.mjs
index a3618fc..c0f1962 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -362,6 +362,7 @@ export default config(
                 "IArguments",
                 "Iterable",
                 "Promise",
+                "ReadonlyMap",
                 "RegExp",
               ],
             },
diff --git a/src/isDeepEqual.ts b/src/isDeepEqual.ts
index 32382ba..e439dfa 100644
--- a/src/isDeepEqual.ts
+++ b/src/isDeepEqual.ts
@@ -7,9 +7,9 @@ import { purry } from "./purry";
  * objects all props will be compared recursively. The built-in Date and RegExp
  * are special-cased and will be compared by their values.
  *
- * !IMPORTANT: Maps, Sets and TypedArrays, and symbol properties of objects  are
- * not supported right now and might result in unexpected behavior. Please open
- * an issue in the Remeda github project if you need support for these types.
+ * !IMPORTANT: Sets, TypedArrays, and symbol properties of objects are not
+ * supported right now and might result in unexpected behavior. Please open an
+ * issue in the Remeda github project if you need support for these types.
  *
  * The result would be narrowed to the second value so that the function can be
  * used as a type guard.
@@ -38,9 +38,9 @@ export function isDeepEqual<T, S extends T = T>(data: T, other: S): boolean;
  * objects all props will be compared recursively. The built-in Date and RegExp
  * are special-cased and will be compared by their values.
  *
- * !IMPORTANT: Maps, Sets and TypedArrays, and symbol properties of objects  are
- * not supported right now and might result in unexpected behavior. Please open
- * an issue in the Remeda github project if you need support for these types.
+ * !IMPORTANT: Sets, TypedArrays, and symbol properties of objects are not
+ * supported right now and might result in unexpected behavior. Please open an
+ * issue in the Remeda github project if you need support for these types.
  *
  * The result would be narrowed to the second value so that the function can be
  * used as a type guard.
@@ -121,6 +121,10 @@ function isDeepEqualImplementation<T, S>(data: S | T, other: S): data is S {
     return data.toString() === (other as unknown as RegExp).toString();
   }
 
+  if (data instanceof Map) {
+    return isDeepEqualMaps(data, other as unknown as Map<unknown, unknown>);
+  }
+
   // At this point we only know that the 2 objects share a prototype and are not
   // any of the previous types. They could be plain objects (Object.prototype),
   // they could be classes, they could be other built-ins, or they could be
@@ -146,3 +150,27 @@ function isDeepEqualImplementation<T, S>(data: S | T, other: S): data is S {
 
   return true;
 }
+
+function isDeepEqualMaps(
+  data: ReadonlyMap<unknown, unknown>,
+  other: ReadonlyMap<unknown, unknown>,
+): boolean {
+  if (data.size !== other.size) {
+    return false;
+  }
+
+  // TODO: Once we bump our typescript target we can iterate over the map keys and values directly.
+  const keys = Array.from(data.keys());
+
+  for (const key of keys) {
+    if (!other.has(key)) {
+      return false;
+    }
+
+    if (!isDeepEqualImplementation(data.get(key), other.get(key))) {
+      return false;
+    }
+  }
+
+  return true;
+}


Test patch:
diff --git a/src/isDeepEqual.test.ts b/src/isDeepEqual.test.ts
index 68bd93a..3c7a461 100644
--- a/src/isDeepEqual.test.ts
+++ b/src/isDeepEqual.test.ts
@@ -155,6 +155,69 @@ describe("arrays", () => {
   });
 });
 
+describe("Maps", () => {
+  it("works on shallow equal maps", () => {
+    expect(isDeepEqual(new Map([["a", 1]]), new Map([["a", 1]]))).toBe(true);
+  });
+
+  test("two empty Maps should be equal", () => {
+    expect(isDeepEqual(new Map(), new Map())).toBe(true);
+  });
+
+  it("two Maps with different size should not be equal", () => {
+    expect(isDeepEqual(new Map(), new Map([["a", 1]]))).toBe(false);
+  });
+
+  it("two Maps with different keys shoud not be equal", () => {
+    expect(
+      isDeepEqual(
+        new Map([
+          ["a", 1],
+          ["c", 2],
+        ]),
+        new Map([
+          ["a", 1],
+          ["b", 2],
+        ]),
+      ),
+    ).toBe(false);
+  });
+
+  it("two maps with the same keys but with different values should not be equal", () => {
+    expect(
+      isDeepEqual(
+        new Map([
+          ["a", 1],
+          ["b", 3],
+          ["c", 2],
+        ]),
+        new Map([
+          ["a", 1],
+          ["b", 2],
+          ["c", 2],
+        ]),
+      ),
+    ).toBe(false);
+  });
+
+  it("two Maps with the same non primitives data should be equal", () => {
+    expect(
+      isDeepEqual(
+        new Map([
+          ["a", { a: [1, 2, 3] }],
+          ["b", { a: [3] }],
+          ["c", { b: [4] }],
+        ]),
+        new Map([
+          ["a", { a: [1, 2, 3] }],
+          ["b", { a: [3] }],
+          ["c", { b: [4] }],
+        ]),
+      ),
+    ).toBe(true);
+  });
+});
+
 describe("Date objects", () => {
   test("equal date objects", () => {
     expect(