Repository: remeda/remeda
Pull request #640 (2024-04-14 12:43:22)

feat(isDeepEqual): add supports for Set
add supports for Set

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/640/builds/494570) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 63845509a576775bd94246d492c2ec35991df24d:

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

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/remeda/remeda/pull/640?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 [(`c1a8d08`)](https://app.codecov.io/gh/remeda/remeda/pull/640?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=remeda).
> Report is 87 commits behind head on master.


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


```diff
@@            Coverage Diff             @@
##           master     #640      +/-   ##
==========================================
+ Coverage   98.41%   98.59%   +0.18%     
==========================================
  Files         127      154      +27     
  Lines        7954    10991    +3037     
  Branches      724      915     +191     
==========================================
+ Hits         7828    10837    +3009     
- Misses        123      151      +28     
  Partials        3        3              
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/remeda/remeda/pull/640?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 c0f1962..185a95f 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -364,6 +364,7 @@ export default config(
                 "Promise",
                 "ReadonlyMap",
                 "RegExp",
+                "ReadonlySet",
               ],
             },
           ],
diff --git a/src/isDeepEqual.ts b/src/isDeepEqual.ts
index 405f642..d80008f 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: 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.
+ * !IMPORTANT: 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.
@@ -94,6 +94,10 @@ function isDeepEqualImplementation<T, S>(data: S | T, other: S): data is S {
     return false;
   }
 
+  if (data instanceof Set) {
+    return isDeepEqualSets(data, other as unknown as Set<unknown>);
+  }
+
   if (Array.isArray(data)) {
     if (data.length !== (other as ReadonlyArray<unknown>).length) {
       return false;
@@ -174,3 +178,33 @@ function isDeepEqualMaps(
 
   return true;
 }
+
+function isDeepEqualSets(
+  data: ReadonlySet<unknown>,
+  other: ReadonlySet<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 dataArr = Array.from(data.values());
+  const otherArr = Array.from(other.values());
+
+  for (const dataItem of dataArr) {
+    let isFound = false;
+
+    for (let i = 0; i < otherArr.length; i++) {
+      if (isDeepEqualImplementation(dataItem, otherArr[i])) {
+        isFound = true;
+        otherArr.splice(i, 1);
+        break;
+      }
+    }
+    if (!isFound) {
+      return false;
+    }
+  }
+
+  return true;
+}


Test patch:
diff --git a/src/isDeepEqual.test.ts b/src/isDeepEqual.test.ts
index f4bc743..bb4b538 100644
--- a/src/isDeepEqual.test.ts
+++ b/src/isDeepEqual.test.ts
@@ -128,6 +128,53 @@ describe("objects", () => {
   });
 });
 
+describe("Sets", () => {
+  test("two empty sets should be equal", () => {
+    expect(isDeepEqual(new Set(), new Set())).toBe(true);
+  });
+
+  test("two sets of the same lenght should not be equal", () => {
+    expect(isDeepEqual(new Set([1]), new Set([1, 2]))).toBe(false);
+  });
+
+  test("two sets of with different primitive content should not be equal", () => {
+    expect(isDeepEqual(new Set([1, 2, 4]), new Set([1, 2, 3]))).toBe(false);
+  });
+
+  test("two sets of with the same primitive content should equal", () => {
+    expect(
+      isDeepEqual(new Set([{ a: 1 }, { b: 3 }]), new Set([{ b: 3 }, { a: 1 }])),
+    ).toBe(true);
+  });
+
+  test("two sets with duplicated non primitive content should not be equal", () => {
+    expect(
+      isDeepEqual(
+        new Set([{ a: 1 }, { b: 3 }, { a: 1 }]),
+        new Set([{ b: 3 }, { a: 1 }, { b: 3 }]),
+      ),
+    ).toBe(false);
+  });
+
+  test("two sets of Maps with the same values should be equal", () => {
+    expect(
+      isDeepEqual(
+        new Set([new Map([["a", 123]]), new Map([["b", 456]])]),
+        new Set([new Map([["b", 456]]), new Map([["a", 123]])]),
+      ),
+    ).toBe(true);
+  });
+
+  test("two sets with more than two items that are all equal should be equal", () => {
+    expect(
+      isDeepEqual(
+        new Set([{ a: 1 }, { b: 3 }, { c: 4 }, { a: 1 }]),
+        new Set([{ b: 3 }, { c: 4 }, { a: 1 }, { a: 1 }]),
+      ),
+    ).toBe(true);
+  });
+});
+
 describe("arrays", () => {
   test("two empty arrays are equal", () => {
     expect(isDeepEqual([], [])).toBe(true);