Package Exports
- fast-equals
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (fast-equals) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
fast-equals
Perform blazing fast equality comparisons (either deep or shallow) on two objects passed. It has no dependencies, and is ~1.3kB when minified and gzipped.
Unlike most equality validation libraries, the following types are handled out-of-the-box:
NaNDateobjectsRegExpobjectsMap/SetiterablesPromiseobjectsreactelements
Starting with version 1.5.0, circular objects are supported for both deep and shallow equality (see circularDeepEqual and circularShallowEqual). You can also create a custom nested comparator, for specific scenarios (see below).
Table of contents
Usage
You can either import the full object:
import fe from "fast-equals";
console.log(fe.deep({ foo: "bar" }, { foo: "bar" })); // trueOr the individual imports desired:
import { deepEqual } from "fast-equals";
console.log(deepEqual({ foo: "bar" }, { foo: "bar" })); // trueAvailable methods
deepEqual
Aliased on the default export as fe.deep
Performs a deep equality comparison on the two objects passed and returns a boolean representing the value equivalency of the objects.
import { deepEqual } from "fast-equals";
const objectA = { foo: { bar: "baz" } };
const objectB = { foo: { bar: "baz" } };
console.log(objectA === objectB); // false
console.log(deepEqual(objectA, objectB)); // trueshallowEqual
Aliased on the default export as fe.shallow
Performs a shallow equality comparison on the two objects passed and returns a boolean representing the value equivalency of the objects.
import { shallowEqual } from "fast-equals";
const nestedObject = { bar: "baz" };
const objectA = { foo: nestedObject };
const objectB = { foo: nestedObject };
const objectC = { foo: { bar: "baz" } };
console.log(objectA === objectB); // false
console.log(shallowEqual(objectA, objectB)); // true
console.log(shallowEqual(objectA, objectC)); // falsesameValueZeroEqual
Aliased on the default export as fe.sameValueZero
Performs a SameValueZero comparison on the two objects passed and returns a boolean representing the value equivalency of the objects. In simple terms, this means either strictly equal or both NaN.
import { sameValueZeroEqual } from "fast-equals";
const mainObject = { foo: NaN, bar: "baz" };
const objectA = "baz";
const objectB = NaN;
const objectC = { foo: NaN, bar: "baz" };
console.log(sameValueZeroEqual(mainObject.bar, objectA)); // true
console.log(sameValueZeroEqual(mainObject.foo, objectB)); // true
console.log(sameValueZeroEqual(mainObject, objectC)); // falsecircularDeepEqual
Aliased on the default export as fe.circularDeep
Performs the same comparison as deepEqual but supports circular objects. It is slower than deepEqual, so only use if you know circular objects are present.
function Circular(value) {
this.me = {
deeply: {
nested: {
reference: this
}
},
value
};
}
console.log(circularDeepEqual(new Circular("foo"), new Circular("foo"))); // true
console.log(circularDeepEqual(new Circular("foo"), new Circular("bar"))); // falsecircularShallowEqual
Aliased on the default export as fe.circularShallow
Performs the same comparison as shallowequal but supports circular objects. It is slower than shallowEqual, so only use if you know circular objects are present.
const array = ["foo"];
array.push(array);
console.log(circularShallowEqual(array, ["foo", array])); // true
console.log(circularShallowEqual(array, [array])); // falsecreateCustomEqual
Aliased on the default export as fe.createCustom
Creates a custom equality comparator that will be used on nested values in the object. Unlike deepEqual and shallowEqual, this is a partial-application function that will receive the internal comparator and should return a function that compares two objects.
The signature is as follows:
createCustomEqual(deepEqual: function) => (objectA: any, objectB: any, meta: any) => boolean;The meta parameter is whatever you want it to be. It will be passed through to all equality checks, and is meant specifically for use with custom equality methods. For example, with the circularDeepEqual and circularShallowEqual methods, it is used to pass through a cache of processed objects.
An example for a custom equality comparison that also checks against values in the meta object:
import { createCustomEqual } from "fast-equals";
const isDeepEqualOrFooMatchesMeta = createCustomEqual(deepEqual => {
return (objectA, objectB, meta) => {
return (
objectA.foo === meta ||
objectB.foo === meta ||
deepEqual(objectA, objectB, meta)
);
};
});
const objectA = { foo: "bar" };
const objectB = { foo: "baz" };
const meta = "bar";
console.log(isDeepEqualOrFooMatchesMeta(objectA, objectB)); // trueBenchmarks
All benchmarks were performed on an i7 8-core Arch Linux laptop with 16GB of memory using NodeJS version 8.11.1, and are based on averages of running comparisons based deep equality on the following object types:
- Primitives (
String,Number,null,undefined) FunctionObjectArrayDateRegExpreactelements- A mixed object with a combination of all the above types
| Operations / second | Relative margin of error | |
|---|---|---|
| fast-equals | 130,726 | 0.79% |
| nano-equal | 103,950 | 0.85% |
| shallow-equal-fuzzy | 101,535 | 0.60% |
| react-fast-compare | 94,724 | 0.82% |
| fast-deep-equal | 92,172 | 0.85% |
| underscore.isEqual | 63,431 | 0.88% |
| fast-equals (circular) | 53,778 | 0.85% |
| deep-equal | 29,739 | 0.54% |
| lodash.isEqual | 26,201 | 0.70% |
| deep-eql | 16,496 | 0.60% |
| assert.deepStrictEqual | 1,565 | 1.04% |
Caveats that impact the benchmark (and accuracy of comparison):
nano-equaldoes not strictly compare object property structure, array length, or object type, norSameValueZeroequality for datesshallow-equal-fuzzydoes not strictly compare object type or regexp values, norSameValueZeroequality for datesfast-deep-equaldoes not supportNaNorSameValueZeroequality for datesreact-fast-comparedoes not supportNaNorSameValueZeroequality for dates, and does not comparefunctionequalityunderscore.isEqualdoes not supportSameValueZeroequality for primitives or datesdeep-equaldoes not supportNaNand does not strictly compare object type, or date / regexp values, nor usesSameValueZeroequality for datesdeep-eqldoes not supportSameValueZeroequality for zero equality (positive and negative zero are not equal)assert.deepStrictEqualdoes not supportNaNorSameValueZeroequality for dates
All of these have the potential of inflating the respective library's numbers in comparison to fast-equals, but it was the closest apples-to-apples comparison I could create of a reasonable sample size. Maps, Promises, and Sets were excluded from the benchmark entirely because no library other than lodash supported their comparison. The same logic applies to react elements (which can be circular objects), but simple elements are non-circular objects so I kept the react comparison very basic to allow it to be included.
Development
Standard practice, clone the repo and npm i to get the dependencies. The following npm scripts are available:
- benchmark => run benchmark tests against other equality libraries
- build => build unminified dist version with source map and NODE_ENV=development via webpack
- build:minified => build minified dist version with NODE_ENV=production via webpack
- clean => run
clean:dist,clean:es, andclean:libscripts - clean:dist => run
rimrafon thedistfolder - clean:es => run
rimrafon theesfolder - clean:lib => run
rimrafon thelibfolder - dev => start webpack playground App
- dist => run
buildandbuild:minifiedscripts - lint => run ESLint on all files in
srcfolder (also runs ondevscript) - lint:fix => run
lintscript, but with auto-fixer - prepublish =>
- prepublish:compile => run
lint,test:coverage,transpile:lib,transpile:es, anddistscripts - start => run
dev - test => run AVA with NODE_ENV=test on all files in
testfolder - test:coverage => run same script as
testwith code coverage calculation vianyc - test:watch => run same script as
testbut keep persistent watcher - transpile:es => run Babel on all files, in
srcfolder (transpiled toesfolder without transpilation of ES2015 export syntax) - transpile:lib => run Babel on all files in
srcfolder (transpiled tolibfolder)