UNPKG

@rimbu/bimap

Version:

A bidirectional immutable Map of keys and values for TypeScript

284 lines 9.26 kB
import { EmptyBase, NonEmptyBase } from '@rimbu/collection-types/map-custom'; import { OptLazy, TraverseState, Update, } from '@rimbu/common'; import { Stream } from '@rimbu/stream'; import { isEmptyStreamSourceInstance } from '@rimbu/stream/custom'; export class BiMapEmpty extends EmptyBase { constructor(context) { super(); this.context = context; } get size() { return 0; } get keyValueMap() { return this.context.keyValueContext.empty(); } get valueKeyMap() { return this.context.valueKeyContext.empty(); } hasKey() { return false; } hasValue() { return false; } getValue(key, otherwise) { return OptLazy(otherwise); } getKey(value, otherwise) { return OptLazy(otherwise); } set(key, value) { return this.addEntry([key, value]); } addEntry(entry) { return new BiMapNonEmptyImpl(this.context, this.context.keyValueContext.of(entry), this.context.valueKeyContext.of([entry[1], entry[0]])); } addEntries(entries) { return this.context.from(entries); } removeKey() { return this; } removeKeyAndGet() { return undefined; } removeKeys() { return this; } removeValue() { return this; } removeValueAndGet() { return undefined; } removeValues() { return this; } updateKeyAtValue() { return this; } updateValueAtKey() { return this; } streamKeys() { return Stream.empty(); } streamValues() { return Stream.empty(); } toBuilder() { return this.context.builder(); } toString() { return `BiMap()`; } toJSON() { return { dataType: this.context.typeTag, value: [], }; } } export class BiMapNonEmptyImpl extends NonEmptyBase { constructor(context, keyValueMap, valueKeyMap) { super(); this.context = context; this.keyValueMap = keyValueMap; this.valueKeyMap = valueKeyMap; } copy(keyValueMap = this.keyValueMap, valueKeyMap = this.valueKeyMap) { if (keyValueMap === this.keyValueMap && valueKeyMap === this.valueKeyMap) return this; return new BiMapNonEmptyImpl(this.context, keyValueMap, valueKeyMap); } copyE(keyValueMap = this.keyValueMap, valueKeyMap = this.valueKeyMap) { if (keyValueMap.nonEmpty() && valueKeyMap.nonEmpty()) { return new BiMapNonEmptyImpl(this.context, keyValueMap, valueKeyMap); } return this.context.empty(); } get size() { return this.keyValueMap.size; } asNormal() { return this; } stream() { return this.keyValueMap.stream(); } streamKeys() { return this.keyValueMap.streamKeys(); } streamValues() { return this.valueKeyMap.streamKeys(); } hasKey(key) { const token = Symbol(); return token !== this.getValue(key, token); } hasValue(value) { const token = Symbol(); return token !== this.getKey(value, token); } getValue(key, otherwise) { return this.keyValueMap.get(key, otherwise); } getKey(value, otherwise) { return this.valueKeyMap.get(value, otherwise); } set(key, value) { return this.addEntry([key, value]); } addEntry(entry) { const [key, value] = entry; const removeKeyResult = this.keyValueMap.removeKeyAndGet(key); const removeValueResult = this.valueKeyMap.removeKeyAndGet(value); if (undefined === removeKeyResult && undefined === removeValueResult) { // key and value were not present return this.copy(this.keyValueMap.addEntry(entry), this.valueKeyMap.set(value, key)); } if (undefined !== removeKeyResult && undefined !== removeValueResult) { const [removedKeyValueMap, oldValue] = removeKeyResult; const [removedValueKeyMap, oldKey] = removeValueResult; // check if existing entry was not same if (Object.is(oldKey, key) && Object.is(oldValue, value)) return this; const newKeyValueMap = removedKeyValueMap .removeKey(oldKey) .addEntry(entry); const newValueKeyMap = removedValueKeyMap .removeKey(oldValue) .set(value, key); return this.copy(newKeyValueMap, newValueKeyMap); } const newKeyValueMap = (undefined === removeValueResult ? this.keyValueMap : this.keyValueMap.removeKey(removeValueResult[1])).addEntry(entry); const newValueKeyMap = (undefined === removeKeyResult ? this.valueKeyMap : this.valueKeyMap.removeKey(removeKeyResult[1])).set(value, key); return this.copy(newKeyValueMap, newValueKeyMap); } addEntries(entries) { if (isEmptyStreamSourceInstance(entries)) return this; const builder = this.toBuilder(); builder.addEntries(entries); return builder.build(); } removeKey(key) { const removeKeyResult = this.keyValueMap.removeKeyAndGet(key); if (undefined === removeKeyResult) return this; const [newKeyValueMap, oldValue] = removeKeyResult; if (this.size === 1) return this.context.empty(); const newValueKeyMap = this.valueKeyMap.removeKey(oldValue); return this.copy(newKeyValueMap.assumeNonEmpty(), newValueKeyMap.assumeNonEmpty()); } removeKeyAndGet(key) { const removeKeyResult = this.keyValueMap.removeKeyAndGet(key); if (undefined === removeKeyResult) return undefined; const [newKeyValueMap, oldValue] = removeKeyResult; if (this.size === 1) return [this.context.empty(), oldValue]; const newValueKeyMap = this.valueKeyMap.removeKey(oldValue); return [ this.copy(newKeyValueMap.assumeNonEmpty(), newValueKeyMap.assumeNonEmpty()), oldValue, ]; } removeKeys(keys) { if (isEmptyStreamSourceInstance(keys)) return this; const builder = this.toBuilder(); builder.removeKeys(keys); return builder.build(); } removeValue(value) { const removeResult = this.valueKeyMap.removeKeyAndGet(value); if (undefined === removeResult) return this; const [newValueKeyMap, oldKey] = removeResult; if (this.size === 1) return this.context.empty(); const newKeyValueMap = this.keyValueMap.removeKey(oldKey); return this.copy(newKeyValueMap.assumeNonEmpty(), newValueKeyMap.assumeNonEmpty()); } removeValueAndGet(value) { const removeValueResult = this.valueKeyMap.removeKeyAndGet(value); if (undefined === removeValueResult) return undefined; const [newValueKeyMap, oldKey] = removeValueResult; if (this.size === 1) return [this.context.empty(), oldKey]; const newKeyValueMap = this.keyValueMap.removeKey(oldKey); return [ this.copy(newKeyValueMap.assumeNonEmpty(), newValueKeyMap.assumeNonEmpty()), oldKey, ]; } removeValues(values) { if (isEmptyStreamSourceInstance(values)) return this; const builder = this.toBuilder(); builder.removeValues(values); return builder.build(); } updateValueAtKey(key, valueUpdate) { const token = Symbol(); const currentValue = this.getValue(key, token); if (token === currentValue) return this; const newValue = Update(currentValue, valueUpdate); if (Object.is(newValue, currentValue)) return this; return this.set(key, newValue); } updateKeyAtValue(keyUpdate, value) { const token = Symbol(); const result = this.getKey(value, token); if (token === result) return this; const newKey = Update(result, keyUpdate); if (Object.is(newKey, result)) return this; return this.set(newKey, value); } forEach(f, options = {}) { const { state = TraverseState() } = options; if (state.halted) return; this.keyValueMap.forEach(f, { state }); } filter(pred, options = {}) { const builder = this.context.builder(); builder.addEntries(this.stream().filter(pred, options)); if (builder.size === this.size) return this; return builder.build(); } toBuilder() { return this.context.createBuilder(this); } toArray() { return this.keyValueMap.toArray(); } toString() { return this.stream().join({ start: `BiMap(`, sep: ', ', end: `)`, valueToString: (entry) => `${entry[0]} <-> ${entry[1]}`, }); } toJSON() { return { dataType: this.context.typeTag, value: this.toArray(), }; } } //# sourceMappingURL=immutable.mjs.map