@rimbu/hashed
Version:
Immutable HashMap and HashSet implementations for TypeScript
519 lines • 22 kB
JavaScript
"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