Skip to content
This repository has been archived by the owner on Jul 9, 2018. It is now read-only.

Add package: @wordpress/is-shallow-equal #110

Merged
merged 3 commits into from
Apr 25, 2018
Merged

Conversation

aduth
Copy link
Member

@aduth aduth commented Apr 20, 2018

Related: WordPress/gutenberg#6313
Related: WordPress/gutenberg#5616

This pull request seeks to add a new package: @wordpress/is-shallow-equal. In my pursuit of the perfect shallow equality utility, I've come to the conclusion that for various reasons, none of the existing solutions are quite perfect. Since a function like isShallowEqual is often used for performance-critical code, anything less than perfection was not adequate. I've enumerated all of these reasons in the included README.md. To the point of performance specifically, contrasted with shallowequal as used in Gutenberg, there is shown to be a ~40% improvement.

View README

Testing instructions:

Verify tests pass:

npm test

@codecov
Copy link

codecov bot commented Apr 20, 2018

Codecov Report

Merging #110 into master will increase coverage by 3.45%.
The diff coverage is 97.14%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #110      +/-   ##
==========================================
+ Coverage   64.07%   67.53%   +3.45%     
==========================================
  Files          42       56      +14     
  Lines         604      693      +89     
  Branches      118      142      +24     
==========================================
+ Hits          387      468      +81     
- Misses        175      183       +8     
  Partials       42       42
Impacted Files Coverage Δ
packages/is-shallow-equal/arrays.js 100% <100%> (ø)
packages/is-shallow-equal/objects.js 100% <100%> (ø)
packages/is-shallow-equal/index.js 96.96% <96.96%> (ø)
packages/url/src/index.js 100% <0%> (ø) ⬆️
packages/wordcount/src/stripSpaces.js 100% <0%> (ø)
...count/src/transposeHTMLEntitiesToCountableChars.js 66.66% <0%> (ø)
packages/wordcount/src/stripHTMLEntities.js 66.66% <0%> (ø)
packages/wordcount/src/stripConnectors.js 66.66% <0%> (ø)
packages/wordcount/src/defaultSettings.js 100% <0%> (ø)
... and 8 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 21f3cea...c8d7b29. Read the comment docs.

4. …be intended for use in non-Facebook projects.
- Sorry, [`fbjs/lib/shallowEqual`](https://www.npmjs.com/package/fbjs).
5. …be the fastest implementation.
- Sorry, _every other solution_.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😂

@@ -0,0 +1,90 @@
var isArray = Array.isArray,
keys = Object.keys;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you using var for performance reasons? Thinking these may create ESlint errors if we add strict rules (const, let).

Copy link
Member Author

@aduth aduth Apr 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you using var for performance reasons? Thinking these may create ESlint errors if we add strict rules (const, let).

I'm using var because I'm effectively opting out of any transpilation (wanting full control over code being executed). I think if we were to introduce such rules, we should apply them to each package's src folder, not necessarily a raw implementation as is done here.


var aKeys = keys( a ),
bKeys = keys( b ),
length = aKeys.length;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we use separate var for each one?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we use separate var for each one?

We can, but my obsessive-yet-likely-often-misled interpretations of benchmark results had me do very specific code style 😄

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But in reality, these are not that important because in general these get mangled, minified in the app bundles.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point, at least for web bundles. There's still application in a Node script, which would use this code as written.

I should note that I'm not even certain this was the reason for the particular style in this case, just a general note that common style best practices which are encouraged more for maintainability's sake were deemphasized here. The file would probably need a fair few number of eslint-disable.

}

var aKeys = keys( a ),
bKeys = keys( b ),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if a for in loop could be better than relying on keys performance wise?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this one is better if the keys length is different?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Object.keys is actually quite fast. Faster than for in: https://jsperf.com/object-keys-iteration/20

And yes, there's a shortcutting opportunity here that we can assume the two values are different if they have keys of unequal length. Also not just for shortcutting, but verifying in case the second object has more keys than the first that we don't miss out on considering those (a = { foo: 1 }, b = { foo: 1, bar: 2 })

@aaronjorbin
Copy link
Member

#111 seeks to solve the codecov lowering by not checking the benchmark script.

aduth and others added 2 commits April 21, 2018 19:44
No need to affect the codecoverage stats with a benchmarking script
Copy link
Collaborator

@youknowriad youknowriad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

Otherwise considered a proper required dependency from perspective of consuming project
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants