Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: Set compares by reference not value #50

Open
ryan-roemer opened this issue Dec 19, 2019 · 6 comments
Open

Bug: Set compares by reference not value #50

ryan-roemer opened this issue Dec 19, 2019 · 6 comments
Labels
upstream Issue with fast-deep-equal

Comments

@ryan-roemer
Copy link
Member

ryan-roemer commented Dec 19, 2019

Upstream fast-deep-equal can't handle equal(new Set(["1", {}]), new Set(["1", {}])) because it compares by reference setA.has(setBElement).

Note that when running the correctness tests during yarn benchmark we get differences with lodash.isEqual which is correct:

--- correctness tests: generic and react ---

react-fast-compare
fast-deep-equal
lodash.isEqual
- different result: lodash.isEqual objects objects with different `toString` functions returning same values are equal
- different result: lodash.isEqual Maps empty maps of different class are not equal
- different result: lodash.isEqual Maps not equal maps (same key "order" - instances of different classes)
- different result: lodash.isEqual Sets empty sets of different class are not equal
- different result: lodash.isEqual Sets not equal sets (same value "order" - instances of different classes)
- different result: lodash.isEqual Sets not equal sets (different instances of objects)

Here's a WIP diff against https://github.com/FormidableLabs/react-fast-compare/tree/experiment/es6 branch that could fix that:

diff --git a/index.js b/index.js
index 2f03bfb..6451b7b 100644
--- a/index.js
+++ b/index.js
@@ -56,11 +56,23 @@ function equal(a, b) {
       return true;
     }
 
+    // There's an upstream bug in `fast-deep-equal` for nested `Set`s
+    // which our tests do cover.
+    var bIt, bI;
     if (hasSet && (a instanceof Set) && (b instanceof Set)) {
       if (a.size !== b.size) return false;
       it = a.entries();
-      for (i = it.next(); !i.done; i = it.next())
-        if (!b.has(i.value[0])) return false;
+      aSetLoop: for (i = it.next(); !i.done; i = it.next()) {
+        if (!b.has(i.value[0])) {
+          // NOTE: Modification to fix nested set issue in `fast-deep-equal`
+          // Add manual iteration of all set B values.
+          bIt = b.entries();
+          for (bI = bIt.next(); !bI.done; bI = bIt.next())
+            if (equal(i.value[0], bI.value[0])) continue aSetLoop;
+
+          return false;
+        }
+      }
       return true;
     }
     // END: Modifications
diff --git a/test/node/tests.js b/test/node/tests.js
index 02336e5..3db4c74 100644
--- a/test/node/tests.js
+++ b/test/node/tests.js
@@ -64,9 +64,57 @@ const react = [
   }
 ];
 
+const extraEs6 = [
+  {
+    description: 'Additional es6 tests',
+    reactSpecific: true,
+    tests: [
+      {
+        description: 'nested Maps with same values are equal',
+        value1: new Map([['one', 1], ['map', new Map([['two', 2]])]]),
+        value2: new Map([['one', 1], ['map', new Map([['two', 2]])]]),
+        equal: true
+      },
+      {
+        description: 'nested Maps with different values are not equal',
+        value1: new Map([['one', 1], ['map', new Map([['two', 2]])]]),
+        value2: new Map([['one', 1], ['map', new Map([['three', 3]])]]),
+        equal: false
+      },
+      // Fails in `fast-deep-equal`
+      {
+        description: 'nested Sets with same values are equal',
+        value1: new Set(['one', new Set(['two'])]),
+        value2: new Set(['one', new Set(['two'])]),
+        equal: true
+      },
+      {
+        description: 'nested Sets with different values are not equal',
+        value1: new Set(['one', new Set(['two'])]),
+        value2: new Set(['one', new Set(['three'])]),
+        equal: false
+      },
+      // Fails in `fast-deep-equal`
+      {
+        description: 'nested Maps + Sets with same values are equal',
+        value1: new Map([['one', 1], ['set', new Set(['one', new Set(['two'])])]]),
+        value2: new Map([['one', 1], ['set', new Set(['one', new Set(['two'])])]]),
+        equal: true
+      },
+      {
+        description: 'nested Maps + Sets with different values are not equal',
+        value1: new Map([['one', 1], ['set', new Set(['one', new Set(['two'])])]]),
+        value2: new Map([['one', 1], ['set', new Set(['one', new Set(['three'])])]]),
+        equal: false
+      }
+    ]
+  }
+];
+
 module.exports = {
   generic,
   es6,
+  extraEs6,
   react,
-  all: [...generic, ...es6, ...react],
+  all: [...generic, ...es6, ...extraEs6, ...react],
 };

Could look to open either upstream or in our project.

chrisbolin pushed a commit that referenced this issue Jan 29, 2020
- Update library to include ES.next support for `Map`, `Set`, `ArrayBuffer`. Part of #36
- Update to `fast-deep-equal@3.1.1` with modified support for ES.next data types.
- Upgrade lots of `devDependenices`
- Use `fast-deep-equal` tests directly in our correctness tests.
- Update CI to modern Node.js versions.
- **Note**: There's a bug / limitation of `Set` comparisons whereby objects are compared by reference not value. Tracked at #50 . In our `yarn benchmark`, `lodash.isEqual` gets test differences because it correctly handles those.
@ryan-roemer
Copy link
Member Author

Now tracked upstream with a potential bugfix: epoberezkin/fast-deep-equal#50

@ryan-roemer ryan-roemer added the upstream Issue with fast-deep-equal label Mar 20, 2020
@ryan-roemer ryan-roemer changed the title Bug: Set compares by reference not value (upstream issue). Bug: Set compares by reference not value Mar 20, 2020
@kale-stew
Copy link
Contributor

Looking at the solution comment on the related issue linked by @ryan-roemer above ^, it appears we can solve this issue by requiring fast-deep-equal/es6 instead of fast-deep-equal.

Is this something we would be willing to change? It seems like there are going to be larger implications updating this dependency, but I don't know that I can immediately identify what they'll be.

cc @chrisbolin

@chrisbolin
Copy link
Contributor

good catch, @kale-stew! I say we don't take it on yet, but that's a path we could go down

@ryan-roemer
Copy link
Member Author

@chrisbolin @kale-stew -- Upstream FDE AFAICT incorrectly closed the issue. Their current master https://github.com/epoberezkin/fast-deep-equal/blob/master/src/index.jst#L34-L39 still does reference comparison and does not do a recursive equality check as my POC experiment code above does.

@ryan-roemer
Copy link
Member Author

And as our general note our source already tracks fast-deep-equal/es6 😉

@kevinbarabash
Copy link

Do we have to way for fast-deep-equal/es6 to fix this before it can be fixed in this repo?

kevinbarabash added a commit to Khan/react-fast-compare that referenced this issue Jan 22, 2024
## Summary:
This PR applies the patch from FormidableLabs#50 to fix an issue with nested Sets not being handled correctly.

Issue: XXX-XXXX

## Test plan:
- yarn
- yarn test

Author: kevinbarabash

Reviewers: jeresig

Required Reviewers:

Approved By: jeresig

Checks:

Pull Request URL: #1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
upstream Issue with fast-deep-equal
Projects
None yet
Development

No branches or pull requests

4 participants