convex-helpers
Version:
A collection of useful code to complement the official convex package.
81 lines (80 loc) • 2.23 kB
JavaScript
// Returns -1 if k1 < k2
// Returns 0 if k1 === k2
// Returns 1 if k1 > k2
export function compareValues(k1, k2) {
return compareAsTuples(makeComparable(k1), makeComparable(k2));
}
function compareAsTuples(a, b) {
if (a[0] === b[0]) {
return compareSameTypeValues(a[1], b[1]);
}
else if (a[0] < b[0]) {
return -1;
}
return 1;
}
function compareSameTypeValues(v1, v2) {
if (v1 === undefined || v1 === null) {
return 0;
}
if (typeof v1 === "bigint" ||
typeof v1 === "number" ||
typeof v1 === "boolean" ||
typeof v1 === "string") {
return v1 < v2 ? -1 : v1 === v2 ? 0 : 1;
}
if (!Array.isArray(v1) || !Array.isArray(v2)) {
throw new Error(`Unexpected type ${v1}`);
}
for (let i = 0; i < v1.length && i < v2.length; i++) {
const cmp = compareAsTuples(v1[i], v2[i]);
if (cmp !== 0) {
return cmp;
}
}
if (v1.length < v2.length) {
return -1;
}
if (v1.length > v2.length) {
return 1;
}
return 0;
}
// Returns an array which can be compared to other arrays as if they were tuples.
// For example, [1, null] < [2, 1n] means null sorts before all bigints
// And [3, 5] < [3, 6] means floats sort as expected
// And [7, [[5, "a"]]] < [7, [[5, "a"], [5, "b"]]] means arrays sort as expected
function makeComparable(v) {
if (v === undefined) {
return [0, undefined];
}
if (v === null) {
return [1, null];
}
if (typeof v === "bigint") {
return [2, v];
}
if (typeof v === "number") {
if (isNaN(v)) {
// Consider all NaNs to be equal.
return [3.5, 0];
}
return [3, v];
}
if (typeof v === "boolean") {
return [4, v];
}
if (typeof v === "string") {
return [5, v];
}
if (v instanceof ArrayBuffer) {
return [6, Array.from(new Uint8Array(v)).map(makeComparable)];
}
if (Array.isArray(v)) {
return [7, v.map(makeComparable)];
}
// Otherwise, it's an POJO.
const keys = Object.keys(v).sort();
const pojo = keys.map((k) => [k, v[k]]);
return [8, pojo.map(makeComparable)];
}