urbit/pkg/npm/api/lib/BigIntOrderedMap.ts
Liam Fitzgerald fc955ab83e
interface: new BigIntOrderedMap
New ordered map implementation that is faster and more space
efficient. Stores the map and keys as a flat POJO, sorting them upon
iteration. The sorting cost is only typically paid once per render
however as the sorted array is cached until the map is mutated. This
implementation appears to play nicer with immer's structural sharing,
reducing the incidence of 'props are equal by value, but not by
reference'.
2021-04-20 13:31:57 +10:00

97 lines
2.1 KiB
TypeScript

import _ from 'lodash';
import bigInt, { BigInteger } from "big-integer";
function sortBigInt(a: BigInteger, b: BigInteger) {
if (a.lt(b)) {
return 1;
} else if (a.eq(b)) {
return 0;
} else {
return -1;
}
}
export default class BigIntOrderedMap<V> implements Iterable<[BigInteger, V]> {
private root: Record<string, V> = {}
private cachedIter: [BigInteger, V][] | null = null;
constructor(items: [BigInteger, V][] = []) {
_.forEach(items, ([key, val]) => {
this.set(key, val);
});
this.generateCachedIter();
}
get size() {
return this.cachedIter?.length ?? Object.keys(this.root).length;
}
get(key: BigInteger) {
return this.root[key.toString()] ?? null;
}
set(key: BigInteger, value: V) {
this.root[key.toString()] = value;
this.cachedIter = null;
}
clear() {
this.cachedIter = null;
this.root = {}
}
has(key: BigInteger) {
return key.toString() in this.root;
}
delete(key: BigInteger) {
const had = this.has(key);
if(had) {
delete this.root[key.toString()];
this.cachedIter = null;
}
return had;
}
[Symbol.iterator](): IterableIterator<[BigInteger, V]> {
let idx = 0;
const result = this.generateCachedIter();
return {
[Symbol.iterator]: this[Symbol.iterator],
next: (): IteratorResult<[BigInteger, V]> => {
if (idx < result.length) {
return { value: result[idx++], done: false };
}
return { done: true, value: null };
},
};
}
peekLargest() {
const sorted = Array.from(this);
return sorted[0] as [BigInteger, V] | null;
}
peekSmallest() {
const sorted = Array.from(this);
return sorted[sorted.length - 1] as [BigInteger, V] | null;
}
keys() {
return Object.keys(this.root).map(k => bigInt(k)).sort(sortBigInt)
}
private generateCachedIter() {
if(this.cachedIter) {
return this.cachedIter;
}
const result = Object.keys(this.root).map(key => {
const num = bigInt(key);
return [num, this.root[key]] as [BigInteger, V];
}).sort(([a], [b]) => sortBigInt(a,b));
this.cachedIter = result;
return result;
}
}