@rimbu/proximity
Version:
Immutable ProximityMap implementation for TypeScript
165 lines (132 loc) • 3.98 kB
text/typescript
import type { Token } from '@rimbu/base';
import type { RMapBase } from '@rimbu/collection-types/map-custom';
import type {
OptLazy,
OptLazyOr,
RelatedTo,
TraverseState,
} from '@rimbu/common';
import { type HashMap } from '@rimbu/hashed/map';
import type { StreamSource } from '@rimbu/stream';
import type { ProximityMap } from '@rimbu/proximity/map';
import { wrapHashMap } from './wrapping.mjs';
export class ProximityMapBuilder<K, V> implements ProximityMap.Builder<K, V> {
private readonly internalBuilder: HashMap.Builder<K, V>;
constructor(
readonly context: ProximityMap.Context<K>,
private source?: ProximityMap.NonEmpty<K, V>
) {
this.internalBuilder = context.hashMapContext.builder();
this.internalBuilder.addEntries(source);
}
get size(): number {
return this.internalBuilder.size;
}
get isEmpty(): boolean {
return this.internalBuilder.isEmpty;
}
/**
* Applying `get()` to the Builder does NOT apply the proximity algorithm - which would
* be pointless at this construction stage; the internal, hash-based builder
* is queried instead
*
*/
get = <UK, O>(key: RelatedTo<K, UK>, otherwise?: OptLazy<O>): V | O => {
if (undefined !== this.source) return this.source.get(key, otherwise!);
return this.internalBuilder.get(key, otherwise!);
};
// prettier-ignore
hasKey = <UK = K,>(key: RelatedTo<K, UK>): boolean => {
return this.internalBuilder.hasKey(key);
};
forEach = (
f: (entry: readonly [K, V], index: number, halt: () => void) => void,
options: { state?: TraverseState } = {}
): void => {
this.internalBuilder.forEach(f, options);
};
addEntry = (entry: readonly [K, V]): boolean => {
const hasChanged = this.internalBuilder.addEntry(entry);
if (hasChanged) {
this.source = undefined;
}
return hasChanged;
};
addEntries = (entries: StreamSource<readonly [K, V]>): boolean => {
const hasChanged = this.internalBuilder.addEntries(entries);
if (hasChanged) {
this.source = undefined;
}
return hasChanged;
};
set = (key: K, value: V): boolean => {
const hasChanged = this.internalBuilder.set(key, value);
if (hasChanged) {
this.source = undefined;
}
return hasChanged;
};
removeKey = <UK, O>(key: RelatedTo<K, UK>, otherwise?: OptLazy<O>): V | O => {
if (this.internalBuilder.hasKey(key)) {
this.source = undefined;
}
return this.internalBuilder.removeKey(key, otherwise!);
};
// prettier-ignore
removeKeys = <UK = K,>(keys: StreamSource<RelatedTo<K, UK>>): boolean => {
const hasChanged = this.internalBuilder.removeKeys(keys);
if (hasChanged) {
this.source = undefined;
}
return hasChanged;
};
modifyAt = (
key: K,
options: {
ifNew?: OptLazyOr<V, typeof Token>;
ifExists?:
| (<V2 extends V = V>(
currentValue: V & V2,
remove: typeof Token
) => V | typeof Token)
| V;
}
): boolean => {
const hasChanged = this.internalBuilder.modifyAt(key, options);
if (hasChanged) {
this.source = undefined;
}
return hasChanged;
};
// prettier-ignore
updateAt = <O,>(
key: K,
update: RMapBase.Update<V>,
otherwise?: OptLazy<O>
): V | O => {
const previousValue = this.internalBuilder.updateAt(
key,
update,
otherwise!
);
const newValue = this.internalBuilder.get(key);
if (!Object.is(previousValue, newValue)) {
this.source = undefined;
}
return previousValue;
};
build = (): ProximityMap<K, V> => {
return this.source !== undefined
? this.source
: wrapHashMap(this.context, this.internalBuilder.build());
};
// prettier-ignore
buildMapValues = <V2,>(
mapFun: (value: V, key: K) => V2
): ProximityMap<K, V2> => {
return wrapHashMap(
this.context,
this.internalBuilder.buildMapValues(mapFun)
);
};
}