betmap
Version:
Better Map()
244 lines (243 loc) • 8.03 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BMap = void 0;
/**
* Better Map()
*/
class BMap extends Map {
constructor() {
super(...arguments);
this._listeners = new Map();
}
/**
* Custom serialization function
*/
toJSON() {
// Convert Map to array of key-value pairs
const mapArray = Array.from(this.entries());
return mapArray;
}
/**
* Register listener
*/
on(event, listener) {
var _a;
if (!this._listeners.has(event)) {
this._listeners.set(event, []);
}
(_a = this._listeners.get(event)) === null || _a === void 0 ? void 0 : _a.push(listener);
return this;
}
/**
* Notify subscribers
*/
_notify(event, entries) {
const eventListeners = this._listeners.get(event);
if (eventListeners != null) {
eventListeners.forEach((listener) => listener(entries));
}
}
/**
* Set key-value, return whether it's new.
*/
_set(key, value) {
const isNewKey = !this.has(key);
super.set(key, value);
return isNewKey;
}
set(key, value) {
var _a;
const isNewKey = this._set(key, value);
if (((_a = this._listeners) === null || _a === void 0 ? void 0 : _a.size) > 0) {
this._notify(isNewKey ? 'add' : 'update', new BMap([[key, value]]));
}
return this;
}
/**
* Batch add/update entries
*/
bSet(entries) {
const newEntries = [];
const updatedEntries = [];
entries.forEach(([key, value]) => this._set(key, value) ? newEntries.push([key, value]) : updatedEntries.push([key, value]));
if (newEntries.length > 0) {
this._notify('add', new BMap(newEntries));
}
if (updatedEntries.length > 0) {
this._notify('update', new BMap(updatedEntries));
}
return this;
}
/**
* Batch get multiple entries from the BMap.
* @param keys An array of keys to retrieve.
* @returns A new BMap containing the requested entries.
*/
bGet(keys) {
const result = new BMap();
for (const key of keys) {
if (this.has(key)) {
result.set(key, this.get(key));
}
}
return result;
}
/**
* Delete by key. Returns deleted entry if key existed
*/
_delete(key) {
if (this.has(key)) {
const value = this.get(key);
super.delete(key);
return [key, value];
}
return undefined;
}
delete(key) {
const entry = this._delete(key);
if (entry != null) {
this._notify('delete', new BMap([[key, entry[1]]]));
return true;
}
return false;
}
/**
* Batch delete entries by key
*/
bDelete(keys) {
const deletedEntries = [];
const result = keys.map(key => {
const entry = this._delete(key);
if (entry != null) {
deletedEntries.push(entry);
return true;
}
return false;
});
if (deletedEntries.length > 0) {
this._notify('delete', new BMap(deletedEntries));
}
return result;
}
clear() {
const entries = new BMap(this);
super.clear();
if (entries.size > 0) {
this._notify('delete', entries);
}
}
/**
* Filter the map based on a predicate function.
* @param predicate A function that determines whether a key-value pair should be included.
* @returns A new BMap containing the filtered key-value pairs.
*/
filter(predicate) {
const filteredEntries = [];
for (const [key, value] of this.entries()) {
if (predicate(key, value)) {
filteredEntries.push([key, value]);
}
}
return new BMap(filteredEntries);
}
/**
* Returns the key-value pair of the first element in the map where predicate is true, and undefined otherwise.
* @param predicate A function that returns true for the first matching key-value pair.
* @returns The first matching key-value pair, or undefined if none is found.
*/
find(predicate) {
for (const [key, value] of this.entries()) {
if (predicate(key, value)) {
return [key, value];
}
}
return undefined;
}
/**
* Check if at least one key-value pair satisfies a condition.
* @param predicate A function that returns true for a matching key-value pair.
* @returns True if at least one matching key-value pair is found, false otherwise.
*/
some(predicate) {
return this.find(predicate) !== undefined;
}
/**
* Check if every key-value pair satisfies a condition.
* @param predicate A function that returns true for a matching key-value pair.
* @returns True if every key-value pair matches the condition, false otherwise.
*/
every(predicate) {
for (const [key, value] of this.entries()) {
if (!predicate(key, value)) {
return false;
}
}
return true;
}
/**
* Map over the entries of the BMap and apply a transformation function.
* @param callback A function that transforms each key/value.
* @returns A new BMap with transformed entries.
*/
mapEntries(callback) {
const newMap = new BMap();
for (const [key, value] of this.entries()) {
const [transformedKey, transformedValue] = callback(key, value, this);
newMap.set(transformedKey, transformedValue);
}
return newMap;
}
/**
* Map over the values of the BMap and apply a transformation function.
* @param callback A function that transforms each value.
* @returns A new BMap with transformed values.
*/
mapValues(callback) {
return this.mapEntries((key, value) => ([key, callback(key, value, this)]));
}
/**
* Map over the keys of the BMap and apply a transformation function.
* @param callback A function that transforms each key.
* @returns A new BMap with transformed keys.
*/
mapKeys(callback) {
return this.mapEntries((key, value) => ([callback(key, value, this), value]));
}
/**
* Sort the entries of the BMap in place.
* @param compareFunction A function that defines the sort order.
* @returns The sorted BMap.
*/
sort(compareFunction) {
const sortedEntries = Array.from(this.entries()).sort((a, b) => compareFunction({ key: a[0], value: a[1] }, { key: b[0], value: b[1] }));
// Clear the current map. Using `super` instead of `this` so that we don't fire a delete event.
super.clear();
// Populate the map with the sorted entries
for (const [key, value] of sortedEntries) {
// Using `super` instead of `this` so that we don't fire an add event.
super.set(key, value);
}
return this;
}
/**
* Merge two maps, resolving conflicts based on a customizable merge strategy.
* @param otherMap The map to merge into the current map.
* @param mergeStrategy A function that defines the merge strategy for conflicts.
* @returns The merged BMap.
*/
merge(otherMap, mergeStrategy) {
const mergedMap = new BMap(this);
for (const [key, incomingValue] of otherMap.entries()) {
if (mergedMap.has(key)) {
const existingValue = mergedMap.get(key);
const newValue = mergeStrategy(key, existingValue, incomingValue);
mergedMap.set(key, newValue);
}
else {
mergedMap.set(key, incomingValue);
}
}
return mergedMap;
}
}
exports.BMap = BMap;