UNPKG

@rimbu/bimap

Version:

A bidirectional immutable Map of keys and values for TypeScript

174 lines 6.65 kB
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