blob: dc48069ca031477e997c5409a075e90cd4b404f7 [file] [log] [blame]
'use strict'
module.exports = shallow
shallow.strict = strict
// TODO a future version of this that drops node 0.x support will
// check for Symbol.iterator, and handle anything that can be passed
// to Array.from()
var hasSet = typeof Set === 'function'
function isSet (object) {
return hasSet && (object instanceof Set)
}
function isMap (object) {
return hasSet && (object instanceof Map)
}
function setSame (a, b) {
var ret = a.size === b.size
if (a.size && ret) {
a.forEach(function (entry) {
if (ret)
ret = b.has(entry)
})
}
return ret
}
function mapSame (a, b, ca, cb, fn) {
var ret = a.size === b.size
if (a.size && ret) {
a.forEach(function (value, key) {
if (ret)
ret = b.has(key)
if (ret)
ret = fn(value, b.get(key), ca, cb)
})
}
return ret
}
function isArguments (object) {
return Object.prototype.toString.call(object) === '[object Arguments]'
}
function arrayFrom (obj) {
return Array.isArray(obj) ? obj
: Array.from ? Array.from(obj)
: Array.prototype.slice.call(obj)
}
function strict (a, b) {
return deeper(a, b, [], [])
}
function deeper (a, b, ca, cb) {
return a === b ? true
: a !== a ? b !== b
: typeof a !== 'object' || typeof b !== 'object' ? false
: a === null || b === null ? false
: Buffer.isBuffer(a) && Buffer.isBuffer(b) ? bufferSame(a, b)
: a instanceof Date && b instanceof Date ? a.getTime() === b.getTime()
: a instanceof RegExp && b instanceof RegExp ? regexpSame(a, b)
: isArguments(a) ?
isArguments(b) && deeper(arrayFrom(a), arrayFrom(b), ca, cb)
: isArguments(b) ? false
: a.constructor !== b.constructor ? false
: isSet(a) && isSet(b) ? setSame(a, b)
: isMap(a) && isMap(b) ? mapSame(a, b, ca, cb, deeper)
: deeperObj(a, b, Object.keys(a), Object.keys(b), ca, cb)
}
function deeperObj (a, b, ka, kb, ca, cb) {
// don't bother with stack acrobatics if there's nothing there
return ka.length === 0 && kb.length === 0 ? true
: ka.length !== kb.length ? false
: deeperObj_(a, b, ka, kb, ca, cb)
}
function deeperObj_ (a, b, ka, kb, ca, cb) {
var ret = true
var cal = ca.length
var go = true
while (cal-- && go) {
if (ca[cal] === a) {
ret = cb[cal] === b
go = false
}
}
if (go) {
ca.push(a)
cb.push(b)
ka.sort()
kb.sort()
for (var j = ka.length - 1; j >= 0 && ret; j--) {
if (ka[j] !== kb[j])
ret = false
}
if (ret) {
var key
for (var k = ka.length - 1; k >= 0 && ret; k--) {
key = ka[k]
if (!deeper(a[key], b[key], ca, cb))
ret = false
}
}
if (ret) {
ca.pop()
cb.pop()
}
}
return ret
}
function shallow (a, b) {
return shallower(a, b, [], [])
}
function regexpSame (a, b) {
return a.source === b.source &&
a.global === b.global &&
a.multiline === b.multiline &&
a.lastIndex === b.lastIndex &&
a.ignoreCase === b.ignoreCase
}
function bufferSame (a, b) {
var ret
if (a.equals) {
ret = a.equals(b)
} else if (a.length !== b.length) {
ret = false
} else {
ret = true
for (var j = 0; j < a.length && ret; j++) {
if (a[j] != b[j])
ret = false
}
}
return ret
}
function shallower (a, b, ca, cb) {
return typeof a !== 'object' && typeof b !== 'object' && a == b ? true
: a === null || b === null ? a == b
: a !== a ? b !== b
: typeof a !== 'object' || typeof b !== 'object' ? false
: Buffer.isBuffer(a) && Buffer.isBuffer(b) ? bufferSame(a, b)
: a instanceof Date && b instanceof Date ? a.getTime() === b.getTime()
: a instanceof RegExp && b instanceof RegExp ? regexpSame(a, b)
: isArguments(a) || isArguments(b) ?
shallower(arrayFrom(a), arrayFrom(b), ca, cb)
: isSet(a) && isSet(b) ? setSame(a, b)
: isMap(a) && isMap(b) ? mapSame(a, b, ca, cb, shallower)
: shallowerObj(a, b, Object.keys(a), Object.keys(b), ca, cb)
}
function shallowerObj (a, b, ka, kb, ca, cb) {
// don't bother with stack acrobatics if there's nothing there
return ka.length === 0 && kb.length === 0 ? true
: ka.length !== kb.length ? false
: shallowerObj_(a, b, ka, kb, ca, cb)
}
function shallowerObj_ (a, b, ka, kb, ca, cb) {
var ret
var cal = ca.length
var go = true
while (cal-- && go) {
if (ca[cal] === a) {
ret = cb[cal] === b
go = false
}
}
if (go) {
ca.push(a)
cb.push(b)
ka.sort()
kb.sort()
ret = true
for (var k = ka.length - 1; k >= 0 && ret; k--) {
if (ka[k] !== kb[k])
ret = false
}
if (ret) {
var key
for (var l = ka.length - 1; l >= 0 && ret; l--) {
key = ka[l]
if (!shallower(a[key], b[key], ca, cb))
ret = false
}
}
if (ret) {
ca.pop()
cb.pop()
}
}
return ret
}