@rimbu/bimap
Version:
A bidirectional immutable Map of keys and values for TypeScript
284 lines • 9.26 kB
JavaScript
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