rubico
Version:
[a]synchronous functional programming
183 lines (160 loc) • 5.2 kB
JavaScript
/**
* rubico v2.6.2
* https://github.com/a-synchronous/rubico
* (c) 2019-2024 Richard Tong
* rubico may be freely distributed under the MIT license.
*/
(function (root, isDeepEqual) {
if (typeof module == 'object') (module.exports = isDeepEqual) // CommonJS
else if (typeof define == 'function') define(() => isDeepEqual) // AMD
else (root.isDeepEqual = isDeepEqual) // Browser
}(typeof globalThis == 'object' ? globalThis : this, (function () { 'use strict'
const __ = Symbol.for('placeholder')
const isArray = Array.isArray
const isPromise = value => value != null && typeof value.then == 'function'
const promiseAll = Promise.all.bind(Promise)
const spread2 = func => function spreading2([arg0, arg1]) {
return func(arg0, arg1)
}
// argument resolver for curry2
const curry2ResolveArg0 = (
baseFunc, arg1,
) => function arg0Resolver(arg0) {
return baseFunc(arg0, arg1)
}
// argument resolver for curry2
const curry2ResolveArg1 = (
baseFunc, arg0,
) => function arg1Resolver(arg1) {
return baseFunc(arg0, arg1)
}
const curry2 = function (baseFunc, arg0, arg1) {
return arg0 == __
? curry2ResolveArg0(baseFunc, arg1)
: curry2ResolveArg1(baseFunc, arg0)
}
const objectKeysLength = object => {
let numKeys = 0
for (const _ in object) {
numKeys += 1
}
return numKeys
}
const symbolIterator = Symbol.iterator
const sameValueZero = function (left, right) {
return left === right || (left !== left && right !== right)
}
const areIteratorsDeepEqual = function (leftIterator, rightIterator) {
let leftIteration = leftIterator.next(),
rightIteration = rightIterator.next()
if (leftIteration.done != rightIteration.done) {
return false
}
while (!leftIteration.done) {
if (!isDeepEqual(leftIteration.value, rightIteration.value)) {
return false
}
leftIteration = leftIterator.next()
rightIteration = rightIterator.next()
}
return rightIteration.done
}
const areObjectsDeepEqual = function (leftObject, rightObject) {
const leftKeysLength = objectKeysLength(leftObject),
rightKeysLength = objectKeysLength(rightObject)
if (leftKeysLength != rightKeysLength) {
return false
}
for (const key in leftObject) {
if (!isDeepEqual(leftObject[key], rightObject[key])) {
return false
}
}
return true
}
const areArraysDeepEqual = function (leftArray, rightArray) {
const length = leftArray.length
if (rightArray.length != length) {
return false
}
let index = -1
while (++index < length) {
if (!isDeepEqual(leftArray[index], rightArray[index])) {
return false
}
}
return true
}
const areValuesDeepEqual = function (left, right) {
const isLeftArray = isArray(left),
isRightArray = isArray(right)
if (isLeftArray || isRightArray) {
return isLeftArray && isRightArray
&& areArraysDeepEqual(left, right)
}
if (left == null || right == null) {
return sameValueZero(left, right)
}
const isLeftString = typeof left == 'string' || left.constructor == String,
isRightString = typeof right == 'string' || right.constructor == String
if (isLeftString || isRightString) {
return sameValueZero(left, right)
}
const isLeftIterable = typeof left[symbolIterator] == 'function',
isRightIterable = typeof right[symbolIterator] == 'function'
if (isLeftIterable || isRightIterable) {
return isLeftIterable && isRightIterable
&& areIteratorsDeepEqual(left[symbolIterator](), right[symbolIterator]())
}
const isLeftObject = left.constructor == Object,
isRightObject = right.constructor == Object
if (isLeftObject || isRightObject) {
return isLeftObject && isRightObject
&& areObjectsDeepEqual(left, right)
}
return sameValueZero(left, right)
}
const isDeepEqual = function (left, right) {
const isLeftResolver = typeof left == 'function',
isRightResolver = typeof right == 'function'
if (isLeftResolver && isRightResolver) {
return function isDeepEqualBy(value) {
const leftValue = left(value),
rightValue = right(value)
const isLeftPromise = isPromise(leftValue),
isRightPromise = isPromise(rightValue)
if (isLeftPromise && isRightPromise) {
return promiseAll([
leftValue,
rightValue,
]).then(spread2(areValuesDeepEqual))
}
if (isLeftPromise) {
return leftValue.then(curry2(areValuesDeepEqual, __, rightValue))
}
if (isRightPromise) {
return rightValue.then(curry2(areValuesDeepEqual, leftValue, __))
}
return areValuesDeepEqual(leftValue, rightValue)
}
}
if (isLeftResolver) {
return function isDeepEqualBy(value) {
const leftValue = left(value)
return isPromise(leftValue)
? leftValue.then(curry2(areValuesDeepEqual, __, right))
: areValuesDeepEqual(leftValue, right)
}
}
if (isRightResolver) {
return function isDeepEqualBy(value) {
const rightValue = right(value)
return isPromise(rightValue)
? rightValue.then(curry2(areValuesDeepEqual, left, __))
: areValuesDeepEqual(left, rightValue)
}
}
return areValuesDeepEqual(left, right)
}
return isDeepEqual
}())))