UNPKG

@rimbu/hashed

Version:

Immutable HashMap and HashSet implementations for TypeScript

519 lines 22 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HashMapCollision = exports.HashMapBlock = exports.HashMapNonEmptyBase = exports.HashMapEmpty = void 0; var tslib_1 = require("tslib"); var base_1 = require("@rimbu/base"); var map_custom_1 = require("@rimbu/collection-types/map-custom"); var common_1 = require("@rimbu/common"); var stream_1 = require("@rimbu/stream"); var custom_1 = require("@rimbu/stream/custom"); var HashMapEmpty = /** @class */ (function (_super) { tslib_1.__extends(HashMapEmpty, _super); function HashMapEmpty(context) { var _this = _super.call(this) || this; _this.context = context; return _this; } HashMapEmpty.prototype.streamKeys = function () { return stream_1.Stream.empty(); }; HashMapEmpty.prototype.streamValues = function () { return stream_1.Stream.empty(); }; HashMapEmpty.prototype.get = function (key, otherwise) { return (0, common_1.OptLazy)(otherwise); }; HashMapEmpty.prototype.hasKey = function () { return false; }; HashMapEmpty.prototype.set = function (key, value) { return this.context.emptyBlock().set(key, value); }; HashMapEmpty.prototype.addEntry = function (entry) { return this.context.emptyBlock().addEntry(entry); }; HashMapEmpty.prototype.addEntries = function (entries) { return this.context.from(entries); }; HashMapEmpty.prototype.removeKeyAndGet = function () { return undefined; }; HashMapEmpty.prototype.removeKey = function () { return this; }; HashMapEmpty.prototype.removeKeys = function () { return this; }; HashMapEmpty.prototype.modifyAt = function (atKey, options) { if (undefined !== options.ifNew) { var value = (0, common_1.OptLazyOr)(options.ifNew, base_1.Token); if (base_1.Token === value) return this; return this.set(atKey, value); } return this; }; HashMapEmpty.prototype.mapValues = function () { return this; }; HashMapEmpty.prototype.updateAt = function () { return this; }; HashMapEmpty.prototype.toBuilder = function () { return this.context.builder(); }; HashMapEmpty.prototype.toString = function () { return "HashMap()"; }; HashMapEmpty.prototype.toJSON = function () { return { dataType: this.context.typeTag, value: [], }; }; return HashMapEmpty; }(map_custom_1.EmptyBase)); exports.HashMapEmpty = HashMapEmpty; var HashMapNonEmptyBase = /** @class */ (function (_super) { tslib_1.__extends(HashMapNonEmptyBase, _super); function HashMapNonEmptyBase() { return _super !== null && _super.apply(this, arguments) || this; } HashMapNonEmptyBase.prototype.asNormal = function () { return this; }; HashMapNonEmptyBase.prototype.streamKeys = function () { return this.stream().map(base_1.Entry.first); }; HashMapNonEmptyBase.prototype.streamValues = function () { return this.stream().map(base_1.Entry.second); }; HashMapNonEmptyBase.prototype.hasKey = function (key) { var token = Symbol(); return token !== this.get(key, token); }; HashMapNonEmptyBase.prototype.set = function (key, value) { return this.addEntry([key, value]); }; HashMapNonEmptyBase.prototype.addEntries = function (entries) { if ((0, custom_1.isEmptyStreamSourceInstance)(entries)) return this; var builder = this.toBuilder(); builder.addEntries(entries); return builder.build().assumeNonEmpty(); }; HashMapNonEmptyBase.prototype.removeKeys = function (keys) { if ((0, custom_1.isEmptyStreamSourceInstance)(keys)) return this; var builder = this.toBuilder(); builder.removeKeys(keys); return builder.build(); }; HashMapNonEmptyBase.prototype.updateAt = function (key, update) { if (!this.context.isValidKey(key)) return this; return this.modifyAt(key, { ifExists: function (value) { return (0, common_1.Update)(value, update); }, }); }; HashMapNonEmptyBase.prototype.removeKey = function (key) { if (!this.context.hasher.isValid(key)) return this; return this.modifyAt(key, { ifExists: function (_, remove) { return remove; }, }); }; HashMapNonEmptyBase.prototype.removeKeyAndGet = function (key) { if (!this.context.hasher.isValid(key)) return undefined; var token = Symbol(); var currentValue = token; var newMap = this.modifyAt(key, { ifExists: function (value, remove) { currentValue = value; return remove; }, }); if (token === currentValue) return undefined; return [newMap, currentValue]; }; HashMapNonEmptyBase.prototype.filter = function (pred, options) { if (options === void 0) { options = {}; } var builder = this.context.builder(); builder.addEntries(this.stream().filter(pred, options)); if (builder.size === this.size) return this; return builder.build(); }; HashMapNonEmptyBase.prototype.toBuilder = function () { return this.context.createBuilder(this); }; HashMapNonEmptyBase.prototype.toString = function () { return this.stream().join({ start: 'HashMap(', sep: ', ', end: ')', valueToString: function (entry) { return "".concat(entry[0], " -> ").concat(entry[1]); }, }); }; HashMapNonEmptyBase.prototype.toJSON = function () { return { dataType: this.context.typeTag, value: this.toArray(), }; }; return HashMapNonEmptyBase; }(map_custom_1.NonEmptyBase)); exports.HashMapNonEmptyBase = HashMapNonEmptyBase; var HashMapBlock = /** @class */ (function (_super) { tslib_1.__extends(HashMapBlock, _super); function HashMapBlock(context, entries, entrySets, size, level) { var _this = _super.call(this) || this; _this.context = context; _this.entries = entries; _this.entrySets = entrySets; _this.size = size; _this.level = level; return _this; } HashMapBlock.prototype.copy = function (entries, entrySets, size) { if (entries === void 0) { entries = this.entries; } if (entrySets === void 0) { entrySets = this.entrySets; } if (size === void 0) { size = this.size; } if (entries === this.entries && entrySets === this.entrySets && size === this.size) { return this; } return new HashMapBlock(this.context, entries, entrySets, size, this.level); }; HashMapBlock.prototype.stream = function () { if (null !== this.entries) { if (null === this.entrySets) return stream_1.Stream.fromObjectValues(this.entries); return stream_1.Stream.fromObjectValues(this.entries).concat(stream_1.Stream.fromObjectValues(this.entrySets).flatMap(function (entrySet) { return entrySet.stream(); })); } if (null === this.entrySets) base_1.RimbuError.throwInvalidStateError(); return stream_1.Stream.fromObjectValues(this.entrySets).flatMap(function (entrySet) { return entrySet.stream(); }); }; HashMapBlock.prototype.get = function (key, otherwise, hash) { if (!this.context.hasher.isValid(key)) return (0, common_1.OptLazy)(otherwise); var keyHash = hash !== null && hash !== void 0 ? hash : this.context.hash(key); var atKeyIndex = this.context.getKeyIndex(this.level, keyHash); if (null !== this.entries && atKeyIndex in this.entries) { var entry = this.entries[atKeyIndex]; if (this.context.eq(entry[0], key)) return entry[1]; return (0, common_1.OptLazy)(otherwise); } if (null !== this.entrySets && atKeyIndex in this.entrySets) { var entrySet = this.entrySets[atKeyIndex]; return entrySet.get(key, otherwise, keyHash); } return (0, common_1.OptLazy)(otherwise); }; HashMapBlock.prototype.addEntry = function (entry, hash) { if (hash === void 0) { hash = this.context.hash(entry[0]); } var atKeyIndex = this.context.getKeyIndex(this.level, hash); if (null !== this.entries && atKeyIndex in this.entries) { var currentEntry = this.entries[atKeyIndex]; if (this.context.eq(entry[0], currentEntry[0])) { if (Object.is(entry[1], currentEntry[1])) return this; var newEntries_1 = base_1.Arr.copySparse(this.entries); newEntries_1[atKeyIndex] = entry; return this.copy(newEntries_1); } var newEntries_2 = base_1.Arr.copySparse(this.entries); delete newEntries_2[atKeyIndex]; var isEmpty = true; for (var _ in newEntries_2) { isEmpty = false; break; } if (isEmpty) newEntries_2 = null; if (this.level < this.context.maxDepth) { var newEntrySet_1 = this.context .block(null, null, 0, this.level + 1) .addEntry(currentEntry) .addEntry(entry, hash); var newEntrySets_1 = null === this.entrySets ? [] : base_1.Arr.copySparse(this.entrySets); newEntrySets_1[atKeyIndex] = newEntrySet_1; return this.copy(newEntries_2, newEntrySets_1, this.size + 1); } var newEntrySet = this.context.collision(this.context.listContext.of(currentEntry, entry)); var newEntrySets = null === this.entrySets ? [] : base_1.Arr.copySparse(this.entrySets); newEntrySets[atKeyIndex] = newEntrySet; return this.copy(newEntries_2, newEntrySets, this.size + 1); } if (null !== this.entrySets && atKeyIndex in this.entrySets) { var currentEntrySet = this.entrySets[atKeyIndex]; var newEntrySet = currentEntrySet.addEntry(entry, hash); if (newEntrySet === currentEntrySet) return this; var newEntrySets = base_1.Arr.copySparse(this.entrySets); newEntrySets[atKeyIndex] = newEntrySet; return this.copy(undefined, newEntrySets, this.size + newEntrySet.size - currentEntrySet.size); } var newEntries = null === this.entries ? [] : base_1.Arr.copySparse(this.entries); newEntries[atKeyIndex] = entry; return this.copy(newEntries, undefined, this.size + 1); }; HashMapBlock.prototype.modifyAt = function (atKey, options, atKeyHash) { if (atKeyHash === void 0) { atKeyHash = this.context.hash(atKey); } var atKeyIndex = this.context.getKeyIndex(this.level, atKeyHash); if (null !== this.entries && atKeyIndex in this.entries) { var currentEntry = this.entries[atKeyIndex]; if (this.context.eq(atKey, currentEntry[0])) { // exact key match if (undefined === options.ifExists) return this; var currentValue = currentEntry[1]; var newValue_1 = options.ifExists instanceof Function ? options.ifExists(currentValue, base_1.Token) : options.ifExists; if (Object.is(newValue_1, currentValue)) return this; var newEntries_3 = base_1.Arr.copySparse(this.entries); if (base_1.Token === newValue_1) { delete newEntries_3[atKeyIndex]; for (var _ in newEntries_3) { return this.copy(newEntries_3, undefined, this.size - 1); } if (this.size === 1) return this.context.empty(); return this.copy(null, undefined, this.size - 1); } newEntries_3[atKeyIndex] = [atKey, newValue_1]; return this.copy(newEntries_3); } // no exact match, but key collision if (undefined === options.ifNew) return this; var newValue_2 = (0, common_1.OptLazyOr)(options.ifNew, base_1.Token); if (base_1.Token === newValue_2) return this; var newEntries_4 = base_1.Arr.copySparse(this.entries); delete newEntries_4[atKeyIndex]; var isEmpty = true; for (var _ in newEntries_4) { isEmpty = false; break; } if (isEmpty) newEntries_4 = null; if (this.level < this.context.maxDepth) { // create next level block var newEntrySet_2 = this.context .block(null, null, 0, this.level + 1) .addEntry(currentEntry) .set(atKey, newValue_2); var newEntrySets_2 = null === this.entrySets ? [] : base_1.Arr.copySparse(this.entrySets); newEntrySets_2[atKeyIndex] = newEntrySet_2; return this.copy(newEntries_4, newEntrySets_2, this.size + 1); } // create collision var newEntry_1 = [atKey, newValue_2]; var newEntrySet = this.context.collision(this.context.listContext.of(currentEntry, newEntry_1)); var newEntrySets = null === this.entrySets ? [] : base_1.Arr.copySparse(this.entrySets); newEntrySets[atKeyIndex] = newEntrySet; return this.copy(newEntries_4, newEntrySets, this.size + 1); } if (null !== this.entrySets && atKeyIndex in this.entrySets) { // key is in entrySet var currentEntrySet = this.entrySets[atKeyIndex]; var newEntrySet = currentEntrySet.modifyAt(atKey, options, atKeyHash); if (newEntrySet === currentEntrySet) return this; if (newEntrySet.size === 1) { var firstEntry = undefined; if (this.context.isHashMapBlock(newEntrySet)) { for (var key in newEntrySet.entries) { firstEntry = newEntrySet.entries[key]; break; } } else { firstEntry = newEntrySet.entries.first(); } var newEntries_5 = null === this.entries ? [] : base_1.Arr.copySparse(this.entries); newEntries_5[atKeyIndex] = firstEntry; var newEntrySets_3 = base_1.Arr.copySparse(this.entrySets); delete newEntrySets_3[atKeyIndex]; return this.copy(newEntries_5, newEntrySets_3, this.size - 1); } var newEntrySets = base_1.Arr.copySparse(this.entrySets); newEntrySets[atKeyIndex] = newEntrySet; return this.copy(undefined, newEntrySets, this.size + newEntrySet.size - currentEntrySet.size); } if (undefined === options.ifNew) return this; var newValue = (0, common_1.OptLazyOr)(options.ifNew, base_1.Token); if (base_1.Token === newValue) return this; var newEntry = [atKey, newValue]; var newEntries = null === this.entries ? [] : base_1.Arr.copySparse(this.entries); newEntries[atKeyIndex] = newEntry; return this.copy(newEntries, undefined, this.size + 1); }; HashMapBlock.prototype.forEach = function (f, options) { if (options === void 0) { options = {}; } var _a = options.state, state = _a === void 0 ? (0, common_1.TraverseState)() : _a; if (state.halted) return; var halt = state.halt; if (null !== this.entries) { for (var key in this.entries) { f(this.entries[key], state.nextIndex(), halt); if (state.halted) return; } } if (null !== this.entrySets) { for (var key in this.entrySets) { this.entrySets[key].forEach(f, { state: state }); if (state.halted) return; } } }; HashMapBlock.prototype.mapValues = function (mapFun) { var newEntries = null === this.entries ? null : base_1.Arr.mapSparse(this.entries, function (e) { return [ e[0], mapFun(e[1], e[0]), ]; }); var newEntrySets = null === this.entrySets ? null : base_1.Arr.mapSparse(this.entrySets, function (es) { return es.mapValues(mapFun); }); return new HashMapBlock(this.context, newEntries, newEntrySets, this.size, this.level); }; HashMapBlock.prototype.toArray = function () { var result = []; if (null !== this.entries) { result = stream_1.Stream.fromObjectValues(this.entries).toArray(); } if (null !== this.entrySets) { stream_1.Stream.fromObjectValues(this.entrySets).forEach(function (entrySet) { result = result.concat(entrySet.toArray()); }); } return result; }; return HashMapBlock; }(HashMapNonEmptyBase)); exports.HashMapBlock = HashMapBlock; var HashMapCollision = /** @class */ (function (_super) { tslib_1.__extends(HashMapCollision, _super); function HashMapCollision(context, entries) { var _this = _super.call(this) || this; _this.context = context; _this.entries = entries; return _this; } Object.defineProperty(HashMapCollision.prototype, "size", { get: function () { return this.entries.length; }, enumerable: false, configurable: true }); HashMapCollision.prototype.copy = function (entries) { if (entries === void 0) { entries = this.entries; } if (entries === this.entries) return this; return new HashMapCollision(this.context, entries); }; HashMapCollision.prototype.stream = function () { return this.entries.stream(); }; HashMapCollision.prototype.get = function (key, otherwise, keyHash) { var _this = this; if (!this.context.hasher.isValid(key)) return (0, common_1.OptLazy)(otherwise); var token = Symbol(); var stream = this.stream(); var foundEntry = stream.find(function (entry) { return _this.context.eq(entry[0], key); }, { otherwise: token, }); if (token === foundEntry) return (0, common_1.OptLazy)(otherwise); return foundEntry[1]; }; HashMapCollision.prototype.addEntry = function (entry, hash) { var _this = this; var currentIndex = this.stream().indexWhere(function (currentEntry) { return _this.context.eq(currentEntry[0], entry[0]); }); if (undefined === currentIndex) { return this.copy(this.entries.append(entry)); } return this.copy(this.entries.updateAt(currentIndex, function (currentEntry) { if (Object.is(currentEntry[1], entry[1])) return currentEntry; return entry; })); }; HashMapCollision.prototype.modifyAt = function (atKey, options, atKeyHash) { var _this = this; var currentIndex = this.stream().indexWhere(function (entry) { return _this.context.eq(entry[0], atKey); }); if (undefined === currentIndex) { if (undefined === options.ifNew) return this; var newValue_3 = (0, common_1.OptLazyOr)(options.ifNew, base_1.Token); if (base_1.Token === newValue_3) return this; var newEntries_6 = this.entries.append([atKey, newValue_3]); return this.copy(newEntries_6); } if (undefined === options.ifExists) return this; var currentEntry = this.entries.get(currentIndex, base_1.RimbuError.throwInvalidStateError); var currentValue = currentEntry[1]; var newValue = options.ifExists instanceof Function ? options.ifExists(currentValue, base_1.Token) : options.ifExists; if (base_1.Token === newValue) { var newEntries_7 = this.entries.remove(currentIndex).assumeNonEmpty(); return this.copy(newEntries_7); } if (Object.is(newValue, currentValue)) return this; var newEntry = [atKey, newValue]; var newEntries = this.entries.updateAt(currentIndex, newEntry); return this.copy(newEntries); }; HashMapCollision.prototype.forEach = function (f, options) { if (options === void 0) { options = {}; } var _a = options.state, state = _a === void 0 ? (0, common_1.TraverseState)() : _a; if (state.halted) return; this.entries.forEach(f, { state: state }); }; HashMapCollision.prototype.mapValues = function (mapFun) { var newEntries = this.entries.map(function (e) { return [ e[0], mapFun(e[1], e[0]), ]; }); return new HashMapCollision(this.context, newEntries); }; HashMapCollision.prototype.toArray = function () { return this.entries.toArray(); }; return HashMapCollision; }(HashMapNonEmptyBase)); exports.HashMapCollision = HashMapCollision; //# sourceMappingURL=immutable.cjs.map