258 lines
5.1 KiB
TypeScript
258 lines
5.1 KiB
TypeScript
import { Equater, MaybeAsyncEquater } from "./types.js";
|
|
|
|
export interface EqualityMap<K, V> extends Iterable<[K, V]> {
|
|
readonly size: number;
|
|
get(key: K): V | undefined;
|
|
set(key: K, value: V): V | undefined;
|
|
contains(key: K): boolean;
|
|
remove(key: K): V | undefined;
|
|
clear(): void;
|
|
keys(): IterableIterator<K>;
|
|
values(): IterableIterator<V>;
|
|
entries(): IterableIterator<[K, V]>;
|
|
}
|
|
|
|
class NativeEqualityMap<K, V> implements EqualityMap<K, V> {
|
|
readonly #map = new Map<K, V>();
|
|
|
|
get size() {
|
|
return this.#map.size;
|
|
}
|
|
|
|
get(key: K) {
|
|
return this.#map.get(key);
|
|
}
|
|
|
|
set(key: K, value: V) {
|
|
const existing = this.get(key);
|
|
this.#map.set(key, value);
|
|
return existing;
|
|
}
|
|
|
|
contains(key: K) {
|
|
return this.#map.has(key);
|
|
}
|
|
|
|
remove(key: K) {
|
|
const existing = this.get(key);
|
|
this.#map.delete(key);
|
|
return existing;
|
|
}
|
|
|
|
clear() {
|
|
this.#map.clear();
|
|
}
|
|
|
|
keys() {
|
|
return this.#map.keys();
|
|
}
|
|
|
|
values() {
|
|
return this.#map.values();
|
|
}
|
|
|
|
entries() {
|
|
return this.#map.entries();
|
|
}
|
|
|
|
[Symbol.iterator]() {
|
|
return this.#map[Symbol.iterator]();
|
|
}
|
|
}
|
|
|
|
class CustomEqualityMap<K, V> implements EqualityMap<K, V> {
|
|
readonly #list: [K, V][] = [];
|
|
readonly #keyComparer: Equater<K>;
|
|
|
|
constructor(keyComparer: Equater<K>) {
|
|
this.#keyComparer = keyComparer;
|
|
}
|
|
|
|
get size() {
|
|
return this.#list.length;
|
|
}
|
|
|
|
get(key: K) {
|
|
for (const entry of this.#list) {
|
|
if (this.#keyComparer(key, entry[0])) {
|
|
return entry[1];
|
|
}
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
set(key: K, value: V) {
|
|
for (const entry of this.#list) {
|
|
if (this.#keyComparer(key, entry[0])) {
|
|
const previous = entry[1];
|
|
entry[1] = value;
|
|
return previous;
|
|
}
|
|
}
|
|
|
|
this.#list.push([key, value]);
|
|
|
|
return undefined;
|
|
}
|
|
|
|
contains(key: K) {
|
|
for (const entry of this.#list) {
|
|
if (this.#keyComparer(key, entry[0])) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
remove(key: K) {
|
|
for (let i = 0; i < this.#list.length; i++) {
|
|
if (this.#keyComparer(key, this.#list[i][0])) {
|
|
const removed = this.#list.splice(i, 1);
|
|
return removed[0][1];
|
|
}
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
clear() {
|
|
this.#list.length = 0;
|
|
}
|
|
|
|
*keys() {
|
|
for (const entry of this.#list) {
|
|
yield entry[0];
|
|
}
|
|
}
|
|
|
|
*values() {
|
|
for (const entry of this.#list) {
|
|
yield entry[1];
|
|
}
|
|
}
|
|
|
|
entries() {
|
|
return this[Symbol.iterator]();
|
|
}
|
|
|
|
*[Symbol.iterator]() {
|
|
for (const entry of this.#list) {
|
|
yield [entry[0], entry[1]] as [K, V]; // no entry mutation allowed!
|
|
}
|
|
}
|
|
}
|
|
|
|
export function createEqualityMap<K, V>(keyComparer?: Equater<K>): EqualityMap<K, V> {
|
|
return keyComparer ? new CustomEqualityMap<K, V>(keyComparer) : new NativeEqualityMap<K, V>();
|
|
}
|
|
|
|
export interface AsyncEqualityMap<K, V> extends Iterable<[K, V]> {
|
|
get(key: K): Promise<V | undefined>;
|
|
set(key: K, value: V): Promise<V | undefined>;
|
|
contains(key: K): Promise<boolean>;
|
|
remove(key: K): Promise<V | undefined>;
|
|
clear(): void;
|
|
}
|
|
|
|
class NativeAsyncEqualityMap<K, V> implements AsyncEqualityMap<K, V> {
|
|
readonly #map = new Map<K, V>();
|
|
|
|
async get(key: K) {
|
|
return this.#map.get(key);
|
|
}
|
|
|
|
async set(key: K, value: V) {
|
|
const existing = await this.get(key);
|
|
this.#map.set(key, value);
|
|
return existing;
|
|
}
|
|
|
|
async contains(key: K) {
|
|
return this.#map.has(key);
|
|
}
|
|
|
|
async remove(key: K) {
|
|
const existing = await this.get(key);
|
|
this.#map.delete(key);
|
|
return existing;
|
|
}
|
|
|
|
clear() {
|
|
this.#map.clear();
|
|
}
|
|
|
|
[Symbol.iterator]() {
|
|
return this.#map[Symbol.iterator]();
|
|
}
|
|
}
|
|
|
|
class CustomAsyncEqualityMap<K, V> implements AsyncEqualityMap<K, V> {
|
|
readonly #list: [K, V][] = [];
|
|
readonly #keyComparer: MaybeAsyncEquater<K>;
|
|
|
|
constructor(keyComparer: MaybeAsyncEquater<K>) {
|
|
this.#keyComparer = keyComparer;
|
|
}
|
|
|
|
async get(key: K) {
|
|
for (const entry of this.#list) {
|
|
if (await this.#keyComparer(key, entry[0])) {
|
|
return entry[1];
|
|
}
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
async set(key: K, value: V) {
|
|
for (const entry of this.#list) {
|
|
if (await this.#keyComparer(key, entry[0])) {
|
|
const previous = entry[1];
|
|
entry[1] = value;
|
|
return previous;
|
|
}
|
|
}
|
|
|
|
this.#list.push([key, value]);
|
|
|
|
return undefined;
|
|
}
|
|
|
|
async contains(key: K) {
|
|
for (const entry of this.#list) {
|
|
if (await this.#keyComparer(key, entry[0])) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
async remove(key: K) {
|
|
for (let i = 0; i < this.#list.length; i++) {
|
|
if (await this.#keyComparer(key, this.#list[i][0])) {
|
|
const removed = this.#list.splice(i, 1);
|
|
return removed[0][1];
|
|
}
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
clear() {
|
|
this.#list.length = 0;
|
|
}
|
|
|
|
*[Symbol.iterator]() {
|
|
for (const entry of this.#list) {
|
|
yield [entry[0], entry[1]] as [K, V]; // no entry mutation allowed!
|
|
}
|
|
}
|
|
}
|
|
|
|
export function createAsyncEqualityMap<K, V>(keyComparer?: MaybeAsyncEquater<K>): AsyncEqualityMap<K, V> {
|
|
return keyComparer ? new CustomAsyncEqualityMap<K, V>(keyComparer) : new NativeAsyncEqualityMap<K, V>();
|
|
}
|