UNPKG

@rimbu/sorted

Version:

Immutable SortedMap and SortedSet implementations for TypeScript

253 lines 9.64 kB
import { Token } from '@rimbu/base'; import { OptLazy, OptLazyOr, Update } from '@rimbu/common'; import { Stream } from '@rimbu/stream'; import { isEmptyStreamSourceInstance } from '@rimbu/stream/custom'; import { SortedBuilder, SortedIndex } from '@rimbu/sorted/common'; export class SortedMapBuilder extends SortedBuilder { constructor(context, source, _entries, _children, size = source?.size ?? 0) { super(); this.context = context; this.source = source; this._entries = _entries; this._children = _children; this.size = size; this.get = (key, otherwise) => { if (!this.context.comp.isComparable(key)) return OptLazy(otherwise); if (undefined !== this.source) return this.source.get(key, otherwise); const entryIndex = this.context.findIndex(key, this.entries); if (entryIndex >= 0) { return this.entries[entryIndex][1]; } if (this.hasChildren) { const childIndex = SortedIndex.next(entryIndex); const child = this.children[childIndex]; return child.get(key, otherwise); } return OptLazy(otherwise); }; // prettier-ignore this.hasKey = (key) => { return Token !== this.get(key, Token); }; this.addEntry = (entry) => { this.checkLock(); const result = this.addEntryInternal(entry); this.normalize(); return result; }; this.addEntries = (source) => { this.checkLock(); return Stream.from(source).filterPure({ pred: this.addEntry }).count() > 0; }; this.set = (key, value) => { return this.addEntry([key, value]); }; this.removeKey = (key, otherwise) => { this.checkLock(); if (!this.context.comp.isComparable(key)) return OptLazy(otherwise); const result = this.removeInternal(key, otherwise); this.normalize(); return result; }; // 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.modifyAt = (key, options) => { this.checkLock(); const result = this.modifyAtInternal(key, options); this.normalize(); return result; }; // prettier-ignore this.updateAt = (key, update, otherwise) => { let result; let found = false; this.modifyAt(key, { ifExists: (value) => { result = value; found = true; return Update(value, update); }, }); if (!found) return OptLazy(otherwise); return result; }; this.build = () => { if (undefined !== this.source) return this.source; if (this.size === 0) return this.context.empty(); if (!this.hasChildren) { return this.context.leaf(this.entries.slice()); } return this.context.inner(this.entries.slice(), this.children.map((child) => child.build()), this.size); }; // prettier-ignore this.buildMapValues = (f) => { if (undefined !== this.source) return this.source.mapValues(f); if (this.size === 0) return this.context.empty(); const newEntries = this.entries.map((entry) => [ entry[0], f(entry[1], entry[0]), ]); if (!this.hasChildren) { return this.context.leaf(newEntries); } return this.context.inner(newEntries, this.children.map((c) => c.buildMapValues(f)), this.size); }; } createNew(source, _entries, _children, size) { return new SortedMapBuilder(this.context, source, _entries, _children, size); } prepareMutate() { if (undefined === this._entries) { if (undefined !== this.source) { if (this.context.isSortedMapEmpty(this.source)) { this._entries = []; this._children = []; } else if (this.context.isSortedMapLeaf(this.source)) { this._entries = this.source.entries.slice(); } else if (this.context.isSortedMapInner(this.source)) { this._entries = this.source.entries.slice(); this._children = this.source.children.map((child) => this.createNew(child)); } } if (undefined === this._entries) { this._entries = []; } } } get children() { this.prepareMutate(); return this._children; } set children(value) { this.prepareMutate(); this.source = undefined; this._children = value; } addEntryInternal(entry) { const entryIndex = this.context.findIndex(entry[0], this.entries); if (entryIndex >= 0) { const currentEntry = this.entries[entryIndex]; if (Object.is(currentEntry[1], entry[1])) return false; this.source = undefined; this.entries[entryIndex] = entry; return true; } const childIndex = SortedIndex.next(entryIndex); if (!this.hasChildren) { this.source = undefined; this.size++; this.entries.splice(childIndex, 0, entry); return true; } const child = this.children[childIndex]; const preSize = child.size; const changed = child.addEntryInternal(entry); if (!changed) return false; this.source = undefined; this.size += child.size - preSize; this.normalizeChildDecrease(childIndex); return true; } removeInternal(key, otherwise) { if (this.size === 0) return OptLazy(otherwise); const entryIndex = this.context.findIndex(key, this.entries); if (entryIndex >= 0) { this.source = undefined; this.size--; if (!this.hasChildren) { const removed = this.entries.splice(entryIndex, 1); return removed[0][1]; } const leftChild = this.children[entryIndex]; const rightChild = this.children[entryIndex + 1]; const removed = this.entries[entryIndex]; if (leftChild.size >= rightChild.size) { this.entries[entryIndex] = leftChild.deleteMax(); this.normalizeChildIncrease(entryIndex); } else { this.entries[entryIndex] = rightChild.deleteMin(); this.normalizeChildIncrease(entryIndex + 1); } return removed[1]; } if (!this.hasChildren) return OptLazy(otherwise); const childIndex = SortedIndex.next(entryIndex); const child = this.children[childIndex]; const preSize = child.size; const token = Symbol(); const oldValue = child.removeInternal(key, token); if (token === oldValue) return OptLazy(otherwise); this.source = undefined; this.size += child.size - preSize; this.normalizeChildIncrease(childIndex); return oldValue; } modifyAtInternal(key, options) { const entryIndex = this.context.findIndex(key, this.entries); if (entryIndex >= 0) { if (undefined === options.ifExists) return false; const currentEntry = this.entries[entryIndex]; const currentValue = currentEntry[1]; const newValue = options.ifExists instanceof Function ? options.ifExists(currentValue, Token) : options.ifExists; if (newValue === currentValue) return false; if (Token === newValue) { return Token !== this.removeInternal(key, Token); } this.source = undefined; const newEntry = [key, newValue]; this.entries[entryIndex] = newEntry; return true; } const childIndex = SortedIndex.next(entryIndex); if (!this.hasChildren) { if (undefined === options.ifNew) return false; const newValue = OptLazyOr(options.ifNew, Token); if (Token === newValue) return false; this.source = undefined; this.size++; this.entries.splice(childIndex, 0, [key, newValue]); return true; } const child = this.children[childIndex]; const preSize = child.size; const changed = child.modifyAtInternal(key, options); if (!changed) return false; this.source = undefined; this.size += child.size - preSize; this.normalizeChildDecrease(childIndex); this.normalizeChildIncrease(childIndex); return changed; } } //# sourceMappingURL=builder.mjs.map