@rimbu/bimap
Version:
A bidirectional immutable Map of keys and values for TypeScript
174 lines • 6.65 kB
JavaScript
import { RimbuError } from '@rimbu/base';
import { OptLazy, TraverseState } from '@rimbu/common';
import { Stream } from '@rimbu/stream';
import { isEmptyStreamSourceInstance } from '@rimbu/stream/custom';
export class BiMapBuilder {
constructor(context, source) {
this.context = context;
this.source = source;
this._lock = 0;
this.getValue = (key, otherwise) => {
if (undefined !== this.source)
return this.source.getValue(key, otherwise);
return this.keyValueMap.get(key, otherwise);
};
// prettier-ignore
this.hasKey = (key) => {
const token = Symbol();
return token !== this.getValue(key, token);
};
this.getKey = (value, otherwise) => {
if (undefined !== this.source)
return this.source.getKey(value, otherwise);
return this.valueKeyMap.get(value, otherwise);
};
// prettier-ignore
this.hasValue = (value) => {
const token = Symbol();
return token !== this.getKey(value, token);
};
this.set = (key, value) => {
return this.addEntry([key, value]);
};
this.addEntry = (entry) => {
this.checkLock();
const [key, value] = entry;
const token = Symbol();
const oldValue = this.keyValueMap.get(key, token);
const oldKey = this.valueKeyMap.get(value, token);
if (token !== oldKey && token !== oldValue) {
if (Object.is(oldKey, key) && Object.is(oldValue, value))
return false;
this.keyValueMap.removeKey(oldKey);
this.keyValueMap.addEntry(entry);
this.valueKeyMap.removeKey(oldValue);
this.valueKeyMap.set(value, key);
}
else if (token !== oldValue) {
if (!Object.is(oldValue, value)) {
this.valueKeyMap.removeKey(oldValue);
this.valueKeyMap.set(value, key);
}
this.keyValueMap.addEntry(entry);
}
else if (token !== oldKey) {
if (!Object.is(oldKey, key)) {
this.keyValueMap.removeKey(oldKey);
this.keyValueMap.addEntry(entry);
}
this.valueKeyMap.set(value, key);
}
else {
this.keyValueMap.addEntry(entry);
this.valueKeyMap.set(value, key);
}
this.source = undefined;
return true;
};
this.addEntries = (source) => {
this.checkLock();
return Stream.from(source).filterPure({ pred: this.addEntry }).count() > 0;
};
this.removeKey = (key, otherwise) => {
this.checkLock();
if (!this.context.keyValueContext.isValidKey(key)) {
return OptLazy(otherwise);
}
const token = Symbol();
const value = this.keyValueMap.removeKey(key, token);
if (token === value)
return OptLazy(otherwise);
this.valueKeyMap.removeKey(value);
this.source = undefined;
return value;
};
// prettier-ignore
this.removeKeys = (keys) => {
this.checkLock();
if (isEmptyStreamSourceInstance(keys))
return false;
const notFound = Symbol();
return (Stream.from(keys)
.mapPure(this.removeKey, notFound)
.countElement(notFound, { negate: true }) > 0);
};
this.removeValue = (value, otherwise) => {
this.checkLock();
if (!this.context.valueKeyContext.isValidKey(value)) {
return OptLazy(otherwise);
}
const token = Symbol();
const key = this.valueKeyMap.removeKey(value, token);
if (token === key)
return OptLazy(otherwise);
this.keyValueMap.removeKey(key);
this.source = undefined;
return key;
};
// prettier-ignore
this.removeValues = (values) => {
this.checkLock();
if (isEmptyStreamSourceInstance(values))
return false;
const notFound = Symbol();
return (Stream.from(values)
.mapPure(this.removeValue, notFound)
.countElement(notFound, { negate: true }) > 0);
};
this.forEach = (f, options = {}) => {
const { state = TraverseState() } = options;
this._lock++;
if (!this.isEmpty && !state.halted) {
if (undefined !== this.source)
this.source.forEach(f, { state });
else
this.keyValueMap.forEach(f, { state });
}
this._lock--;
};
this.build = () => {
if (undefined !== this.source)
return this.source;
if (this.size === 0)
return this.context.empty();
return this.context.createNonEmptyImpl(this.keyValueMap.build().assumeNonEmpty(), this.valueKeyMap.build().assumeNonEmpty());
};
}
checkLock() {
if (this._lock)
RimbuError.throwModifiedBuilderWhileLoopingOverItError();
}
get keyValueMap() {
if (undefined === this._keyValueMap) {
if (undefined === this.source) {
this._keyValueMap = this.context.keyValueContext.builder();
this._valueKeyMap = this.context.valueKeyContext.builder();
}
else {
this._keyValueMap = this.source.keyValueMap.toBuilder();
this._valueKeyMap = this.source.valueKeyMap.toBuilder();
}
}
return this._keyValueMap;
}
get valueKeyMap() {
if (undefined === this._valueKeyMap) {
if (undefined === this.source) {
this._keyValueMap = this.context.keyValueContext.builder();
this._valueKeyMap = this.context.valueKeyContext.builder();
}
else {
this._keyValueMap = this.source.keyValueMap.toBuilder();
this._valueKeyMap = this.source.valueKeyMap.toBuilder();
}
}
return this._valueKeyMap;
}
get size() {
return this.source?.size ?? this.keyValueMap.size;
}
get isEmpty() {
return this.size === 0;
}
}
//# sourceMappingURL=builder.mjs.map