mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-19 12:51:51 +03:00
fc955ab83e
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'.
97 lines
2.1 KiB
TypeScript
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;
|
|
}
|
|
}
|
|
|