pop-compare
Version:
Polymorphic deep comparison operator for arbitrary values
79 lines (71 loc) • 2.93 kB
JavaScript
/**
Determines the order in which any two objects should be sorted by returning
a number that has an analogous relationship to zero as the left value to
the right. That is, if the left is "less than" the right, the returned
value will be "less than" zero, where "less than" may be any other
transitive relationship.
<p>Arrays are compared by the first diverging values, or by length.
<p>Any two values that are incomparable return zero. As such,
<code>equals</code> should not be implemented with <code>compare</code>
since incomparability is indistinguishable from equality.
<p>Sorts strings lexicographically. This is not suitable for any
particular international setting. Different locales sort their phone books
in very different ways, particularly regarding diacritics and ligatures.
<p>If the given object is an instance of a type that implements a method
named "compare", this function defers to the instance. The method does not
need to be an owned property to distinguish it from an object literal since
object literals are incomparable. Unlike <code>Object</code> however,
<code>Array</code> implements <code>compare</code>.
@param {Any} left
@param {Any} right
@returns {Number} a value having the same transitive relationship to zero
as the left and right values.
*/
module.exports = compare;
function compare(a, b, compare) {
var difference;
// unbox objects
// mercifully handles the Date case
if (a && typeof a.valueOf === "function") {
a = a.valueOf();
}
if (b && typeof b.valueOf === "function") {
b = b.valueOf();
}
// x !== x is only true if x is NaN. NaN is "incomparable" and both
// equivalent and incomparable values always return 0.
if (a === b || a !== a || b !== b)
return 0;
var aType = typeof a;
var bType = typeof b;
if (aType === "number" && bType === "number")
return a - b;
if (aType === "string" && bType === "string")
return a < b ? -Infinity : Infinity;
// the possibility of equality elimiated above
compare = compare || module.exports;
if (Array.isArray(a) && Array.isArray(b)) {
for (var index in a) {
if (!(index in b)) {
return Infinity;
} else {
difference = compare(a[index], b[index], compare);
if (difference) {
return difference;
}
}
}
for (var index in b) {
if (!(index in a)) {
return -Infinity;
}
}
return a.length - b.length;
}
if (a && typeof a.compare === "function")
return a.compare(b, compare);
// not commutative, the relationship is reversed
if (b && typeof b.compare === "function")
return -b.compare(a, compare);
return 0;
}