deep-equality-data-structures
Version:
Javascript data structures (e.g., Map, Set) that support deep object equality
172 lines (171 loc) • 5.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DeepMap = void 0;
const errors_1 = require("./errors");
const normalizer_1 = require("./normalizer");
/**
* A Map implementation that supports deep equality for object keys.
*/
class DeepMap extends Map {
// NOTE: This is actually a thin wrapper. We're not using super other than to drive the (typed) API contract.
/**
* @param entries optional list of key-value pairs to initialize the map
* @param options configuration options
*/
constructor(entries, options = {}) {
super();
this.options = options;
this.normalizer = new normalizer_1.Normalizer(options);
const transformedEntries = entries
? entries.map(([key, val]) => [this.normalizeKey(key), { key, val }])
: null;
this.map = new Map(transformedEntries);
}
/**
* Getter for number of kev-value pairs in the map.
* @inheritdoc
*/
get size() {
return this.map.size;
}
/**
* Returns true if the given key is present in the map.
* @inheritdoc
*/
has(key) {
return this.map.has(this.normalizeKey(key));
}
/**
* @inheritdoc
*/
set(key, val) {
this.map.set(this.normalizeKey(key), { key, val });
return this;
}
/**
* @inheritdoc
*/
get(key) {
var _a;
return (_a = this.map.get(this.normalizeKey(key))) === null || _a === void 0 ? void 0 : _a.val;
}
/**
* @inheritdoc
*/
delete(key) {
return this.map.delete(this.normalizeKey(key));
}
/**
* Clear all key-value pairs from the map.
* @inheritdoc
*/
clear() {
this.map.clear();
}
/**
* @inheritdoc
*/
forEach(callbackfn) {
this.map.forEach((pair, _key, _internalMap) => {
callbackfn(pair.val, pair.key, this);
});
}
/**
* @yields the next key-value pair in the map
* @inheritdoc
*/
*[Symbol.iterator]() {
for (const [_hashStr, pair] of this.map[Symbol.iterator]()) {
yield [pair.key, pair.val];
}
}
/**
* @yields the next key-value pair in the map
* @inheritdoc
*/
*entries() {
for (const entry of this[Symbol.iterator]()) {
yield entry;
}
}
/**
* @inheritdoc
*/
*keys() {
for (const [key, _val] of this[Symbol.iterator]()) {
yield key;
}
}
/**
* @inheritdoc
*/
*values() {
for (const [_key, val] of this[Symbol.iterator]()) {
yield val;
}
}
/**
* @param other the map to compare against
* @returns true if the entries of `other` are the same as this map
*/
equals(other) {
this.validateUsingSameOptionsAs(other);
return this.size === other.size && this.contains(other);
}
/**
* @param other the map to compare against
* @returns true if the entries of `other` are all contained in this map
*/
contains(other) {
this.validateUsingSameOptionsAs(other);
return [...other.entries()].every(([key, val]) => this.keyValuePairIsPresentIn(key, val, this));
}
/**
* @param other the map to compare against
* @returns a new map whose keys are the union of keys between `this` and `other` maps.
*
* NOTE: If both maps prescribe the same key, the key-value pair from `this` will be retained.
*/
union(other) {
this.validateUsingSameOptionsAs(other);
return new DeepMap([...other.entries(), ...this.entries()], this.options);
}
/**
* @param other the map to compare against
* @returns a new map containing all key-value pairs in `this` that are also present in `other`.
*/
intersection(other) {
this.validateUsingSameOptionsAs(other);
const intersectingPairs = [...this.entries()].filter(([key, val]) => this.keyValuePairIsPresentIn(key, val, other));
return new DeepMap(intersectingPairs, this.options);
}
/**
* @param other the map to compare against
* @returns a new map containing all key-value pairs in `this` that are not present in `other`.
*/
difference(other) {
this.validateUsingSameOptionsAs(other);
const differencePairs = [...this.entries()].filter(([key, val]) => !this.keyValuePairIsPresentIn(key, val, other));
return new DeepMap(differencePairs, this.options);
}
// PRIVATE/PROTECTED METHODS FOLLOW...
normalizeKey(input) {
return this.normalizer.normalizeKey(input);
}
normalizeValue(input) {
return this.normalizer.normalizeValue(input);
}
validateUsingSameOptionsAs(other) {
if (this.normalizer.getOptionsChecksum() !== other['normalizer'].getOptionsChecksum()) {
throw new errors_1.DeepEqualityDataStructuresError('Structures must use same options for Comparable interface operations');
}
}
/**
* @returns true if the key is present in the provided map w/ the specified value
*/
keyValuePairIsPresentIn(key, val, mapToCheck) {
const checkVal = mapToCheck.get(key);
return checkVal !== undefined && this.normalizeValue(checkVal) === this.normalizeValue(val);
}
}
exports.DeepMap = DeepMap;