@segment/equals
Version:
Check if two values are deeply equivalent
122 lines (100 loc) • 2.6 kB
JavaScript
var type = require('jkroso-type')
// (any, any, [array]) -> boolean
function equal(a, b, memos){
// All identical values are equivalent
if (a === b) return true
var fnA = types[type(a)]
var fnB = types[type(b)]
return fnA && fnA === fnB
? fnA(a, b, memos)
: false
}
var types = {}
// (Number) -> boolean
types.number = function(a, b){
return a !== a && b !== b/*Nan check*/
}
// (function, function, array) -> boolean
types['function'] = function(a, b, memos){
return a.toString() === b.toString()
// Functions can act as objects
&& types.object(a, b, memos)
&& equal(a.prototype, b.prototype)
}
// (date, date) -> boolean
types.date = function(a, b){
return +a === +b
}
// (regexp, regexp) -> boolean
types.regexp = function(a, b){
return a.toString() === b.toString()
}
// (DOMElement, DOMElement) -> boolean
types.element = function(a, b){
return a.outerHTML === b.outerHTML
}
// (textnode, textnode) -> boolean
types.textnode = function(a, b){
return a.textContent === b.textContent
}
// decorate `fn` to prevent it re-checking objects
// (function) -> function
function memoGaurd(fn){
return function(a, b, memos){
if (!memos) return fn(a, b, [])
var i = memos.length, memo
while (memo = memos[--i]) {
if (memo[0] === a && memo[1] === b) return true
}
return fn(a, b, memos)
}
}
types['arguments'] =
types['bit-array'] =
types.array = memoGaurd(arrayEqual)
// (array, array, array) -> boolean
function arrayEqual(a, b, memos){
var i = a.length
if (i !== b.length) return false
memos.push([a, b])
while (i--) {
if (!equal(a[i], b[i], memos)) return false
}
return true
}
types.object = memoGaurd(objectEqual)
// (object, object, array) -> boolean
function objectEqual(a, b, memos) {
if (typeof a.equal == 'function') {
memos.push([a, b])
return a.equal(b, memos)
}
var ka = getEnumerableProperties(a)
var kb = getEnumerableProperties(b)
var i = ka.length
// same number of properties
if (i !== kb.length) return false
// although not necessarily the same order
ka.sort()
kb.sort()
// cheap key test
while (i--) if (ka[i] !== kb[i]) return false
// remember
memos.push([a, b])
// iterate again this time doing a thorough check
i = ka.length
while (i--) {
var key = ka[i]
if (!equal(a[key], b[key], memos)) return false
}
return true
}
// (object) -> array
function getEnumerableProperties (object) {
var result = []
for (var k in object) if (k !== 'constructor') {
result.push(k)
}
return result
}
module.exports = equal