Preserves ordering

This commit is contained in:
Tony Garnock-Jones 2023-12-16 22:13:47 +13:00
parent 9420cc7236
commit 897fc13054
2 changed files with 106 additions and 0 deletions

View File

@ -0,0 +1,105 @@
import { is, isAnnotated } from './is';
import { Bytes } from './bytes';
import { Set, Dictionary } from './dictionary';
import { Embedded, isEmbedded } from './embedded';
import { Float } from './float';
import { Value } from './values';
import { Record } from './record';
export function typeCode<V>(v: Value<V>): number {
if (isAnnotated<V>(v)) v = v.item;
switch (typeof v) {
case 'boolean':
return 0;
case 'number':
case 'bigint':
return 3;
case 'string':
return 4;
case 'symbol':
return 6;
case 'object':
if (Float.isFloat(v)) {
return Float.isSingle(v) ? 1 : 2;
}
if (Bytes.isBytes(v)) return 5;
if (Array.isArray(v)) {
return ('label' in v) ? 7 : 8;
}
if (Set.isSet<V>(v)) return 9;
if (Dictionary.isDictionary<V>(v)) return 10;
if (isEmbedded(v)) return 11;
/* fall through */
default:
throw new Error("Invalid Value<V> in typeCode");
}
}
export function compare<V>(
a: Value<V>,
b: Value<V>,
compare_embedded: (a: V, b: V) => number = (a, b) => is(a, b) ? 0 : a < b ? -1 : 1,
): number {
function cmp(a: Value<V>, b: Value<V>): number {
if (isAnnotated<V>(a)) a = a.item;
if (isAnnotated<V>(b)) b = b.item;
const ta = typeCode(a);
const tb = typeCode(b);
if (ta < tb) return -1;
if (ta > tb) return 1;
switch (ta) {
case 0:
case 3:
case 4: {
const va = a as any;
const vb = b as any;
return va < vb ? -1 : va > vb ? 1 : 0;
}
case 1:
case 2: {
const va = (a as Float).value;
const vb = (b as Float).value;
return va < vb ? -1 : va > vb ? 1 : 0;
}
case 5:
return Bytes.compare(a as Bytes, b as Bytes);
case 6: {
const va = (a as symbol).description!;
const vb = (b as symbol).description!;
return va < vb ? -1 : va > vb ? 1 : 0;
}
case 7: {
const lr = cmp((a as Record<Value<V>, Value<V>[], V>).label,
(b as Record<Value<V>, Value<V>[], V>).label);
if (lr !== 0) return lr;
/* fall through */
}
case 8: {
const va = a as Value<V>[];
const vb = b as Value<V>[];
const l = Math.min(va.length, vb.length)
for (let i = 0; i < l; i++) {
const c = cmp(va[i], vb[i]);
if (c !== 0) return c;
}
return va.length < vb.length ? -1 : va.length > vb.length ? 1 : 0;
}
case 9: {
const va = Array.from(a as Set<V>).sort(cmp);
const vb = Array.from(b as Set<V>).sort(cmp);
return cmp(va, vb);
}
case 10: {
const va = Array.from(a as Dictionary<V>).sort(cmp);
const vb = Array.from(b as Dictionary<V>).sort(cmp);
return cmp(va, vb);
}
case 11:
return compare_embedded((a as Embedded<V>).embeddedValue,
(b as Embedded<V>).embeddedValue);
default:
throw new Error("Invalid typeCode: " + ta);
}
}
return cmp(a, b);
}

View File

@ -14,6 +14,7 @@ export * from './fold';
export * from './fromjs';
export * from './is';
export * from './merge';
export * from './order';
export * from './reader';
export * from './record';
export * from './strip';