@yookue/ts-multi-map
Version:
Multiple key/value map & range map for typescript
528 lines (526 loc) • 13.3 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/util/MultiValueMap.ts
var MultiValueMap_exports = {};
__export(MultiValueMap_exports, {
MultiValueMap: () => MultiValueMap
});
module.exports = __toCommonJS(MultiValueMap_exports);
var MultiValueMap = class {
/**
* Construct a multi value map instance
*
* @param entries the map entries that represented as [K, V[]][]
*
* @example
* ```ts
* const map = new MultiValueMap([
* ['color', ['red', 'green', 'blue']]
* ]);
* ```
*/
constructor(entries) {
this.map = /* @__PURE__ */ new Map();
entries == null ? void 0 : entries.forEach((entry) => {
const [k, vs] = entry;
this.set(k, vs);
});
}
/**
* Construct a multi value map instance
*
* @param entries the map entries that represented as [K, V[]][]
*
* @returns a multi value map instance
*
* @example
* ```ts
* const map = MultiValueMap.of([
* ['color', ['red', 'green', 'blue']]
* ]);
* ```
*/
static of(entries) {
return new MultiValueMap(entries);
}
/**
* Returns the values of the given key
*
* @param key the key to retrieve
* @param defaults the default values if not found
*
* @returns the values of the given key
*
* @example
* ```ts
* const map = MultiValueMap.of([
* ['color', ['red', 'green', 'blue']]
* ]);
* map.get('color'); // ['red', 'green', 'blue']
* map.get('foobar', ['foo', 'bar']); // ['foo', 'bar']
* ```
*/
get(key, defaults) {
return this.map.get(key) ?? defaults;
}
/**
* Sets the values of the given key
*
* @param key the key to set
* @param values the values to set
*
* @example
* ```ts
* map.set('color', ['red', 'green', 'blue']);
* ```
*/
set(key, values) {
this.map.set(key, values);
}
/**
* Push values onto the given key, the values will be appended to the given key
*
* @param key the key to operate
* @param values the values to push
*
* @example
* ```ts
* map.push('color', ['yellow', 'black']);
* ```
*/
push(key, values) {
const array = this.get(key) || [];
array.push(...values);
this.map.set(key, array);
}
/**
* Clears the map
*/
clear() {
this.map.clear();
}
/**
* Returns the keys of the map
*
* @returns the keys of the map
*/
keys() {
return [...this.map.keys()];
}
/**
* Returns the values array of the map
*
* @returns the values array of the map
*/
values() {
return [...this.map.values()];
}
/**
* Returns the key/values entries of the map
*
* @returns the key/values entries of the map
*/
entries() {
return [...this.map.entries()];
}
/**
* Deletes the entry with the given key
*
* @param key the key to delete
*
* @returns whether the entry has been deleted
*
* @example
* ```ts
* map.deleteByKey('color');
* ```
*/
deleteByKey(key) {
return this.map.delete(key);
}
/**
* Deletes all the entries with any of the given keys
*
* @param keys the keys to delete
*
* @returns whether any of the entries has been deleted
*
* @example
* ```ts
* map.deleteByKeys(['color', 'position']);
* ```
*/
deleteByKeys(keys) {
if (!keys.length || this.isEmpty()) {
return false;
}
let result = false;
for (const key of keys) {
if (this.deleteByKey(key)) {
result = true;
}
}
return result;
}
/**
* Deletes the entry/entries with the given value
*
* @param value the value to delete
*
* @returns whether the entry/entries has been deleted
*
* @example
* ```ts
* map.deleteByValue('red');
* ```
*/
deleteByValue(value) {
if (this.isEmpty()) {
return false;
}
let result = false;
for (const [k, vs] of this.entries()) {
if ((vs == null ? void 0 : vs.includes(value)) && this.map.delete(k)) {
result = true;
}
}
return result;
}
/**
* Deletes all the entries with any of the given values
*
* @param values the values to delete
*
* @returns whether any of the entries has been deleted
*
* @example
* ```ts
* map.deleteByValues(['green', 'blue']);
* ```
*/
deleteByValues(values) {
if (!values.length || this.isEmpty()) {
return false;
}
let result = false;
for (const value of values) {
if (this.deleteByValue(value)) {
result = true;
}
}
return result;
}
/**
* Deletes a value for the given key from the map, keeping other values
*
* @param key the key to operate
* @param value the value to delete
*
* @returns whether the value has been removed
*
* @example
* ```ts
* map.deleteValueOfKey('color', 'blue');
* ```
*/
deleteValueOfKey(key, value) {
let array = this.get(key) || [];
if (!array.includes(value)) {
return false;
}
array = array.filter((v) => v !== value);
this.map.set(key, array);
return true;
}
/**
* Processes each entry in the map
*
* @param callback a callback function that processes each entry
* @param thisArg any instance to retrieve 'this' reference in the callback function
*
* @example
* ```ts
* map.forEach((values, key) => {
* console.log(key);
* });
*/
forEach(callback, thisArg) {
this.map.forEach((vs, k) => {
callback(vs, k);
}, thisArg);
}
/**
* Processes each entry in the map with index capability
*
* @param callback a callback function that processes each entry
* @param thisArg any instance to retrieve 'this' reference in the callback function
*
* @example
* ```ts
* map.forEachIndexing((values, key, index) => {
* console.log(index);
* });
*/
forEachIndexing(callback, thisArg) {
let index = 0;
this.map.forEach((vs, k) => {
callback(vs, k, index++);
}, thisArg);
}
/**
* Processes each entry in the map with breakable capability
*
* @param callback a callback function that processes each entry. Returning false indicates to break the map iteration
* @param thisArg any instance to retrieve 'this' reference in the callback function
*
* @example
* ```ts
* map.forEachBreakable((values, key) => {
* return true;
* });
*/
forEachBreakable(callback, thisArg) {
this.map.forEach((vs, k) => {
if (!callback(vs, k)) {
return;
}
}, thisArg);
}
/**
* Returns whether the map contains the given key
*
* @param key the key to check
*
* @returns whether the map contains the given key
*
* @example
* ```ts
* map.hasKey('color');
*/
hasKey(key) {
return this.map.has(key);
}
/**
* Returns whether the map contains the given key/value pair
*
* @param key the key to check
* @param value the value to check
*
* @returns whether the map contains the given key/value pair
*
* @example
* ```ts
* const map = MultiValueMap.of([
* ['color', ['red', 'green', 'blue']]
* ]);
* map.hasKeyValue('color', 'red'); // true
* map.hasKeyValue('color', 'black'); // false
*/
hasKeyValue(key, value) {
return this.isNotEmpty() && this.get(key, []).includes(value);
}
/**
* Returns whether the map contains any of the given keys
*
* @param keys the keys to check
*
* @returns whether the map contains any of the given keys
*
* @example
* ```ts
* const map = MultiValueMap.of([
* ['color', ['red', 'green', 'blue']]
* ]);
* map.hasAnyKeys(['color', 'position']); // true
*/
hasAnyKeys(keys) {
return this.isNotEmpty() && keys.length > 0 && keys.some((item) => this.hasKey(item));
}
/**
* Returns whether the map contains all the given keys
*
* @param keys the keys to check
*
* @returns whether the map contains all the given keys
*
* @example
* ```ts
* map.hasAllKeys('color', 'position');
*/
hasAllKeys(keys) {
return this.isNotEmpty() && keys.length > 0 && keys.every((item) => this.hasKey(item));
}
/**
* Returns whether any entries of the map that contains the given values
*
* @param values the values to check
* @param exact whether matching entry values exactly
*
* @returns whether any entries of the map that contains the given values
*
* @example
* ```ts
* const map = MultiValueMap.of([
* ['color', ['red', 'green', 'blue']],
* ['position', ['top', 'right', 'bottom', 'left']]
* ]);
* map.hasValue(['red', 'black'], true); // false
* map.hasValue(['top', 'right'], true); // false
* map.hasValue(['top', 'right'], false); // true
*/
hasValue(values, exact = true) {
if (!values.length || this.isEmpty()) {
return false;
}
for (const vs of this.map.values()) {
const result = exact ? values.length === vs.length && values.every((item) => vs.includes(item)) : values.some((item) => vs.includes(item));
if (result) {
return true;
}
}
return false;
}
/**
* Returns whether the map contains any of the given values
*
* @param values the values to check
* @param exact whether matching entry values exactly
*
* @returns whether the map contains any of the given values
*
* @example
* ```ts
* const map = MultiValueMap.of([
* ['color', ['red', 'green', 'blue']],
* ['position', ['top', 'right', 'bottom', 'left']]
* ]);
* map.hasAnyValues([['red', 'black'], ['green', 'blue']]); // false
* map.hasAnyValues([['top', 'right'], ['top', 'right', 'bottom', 'left']]); // true
*/
hasAnyValues(values, exact = true) {
return this.isNotEmpty() && values.length > 0 && values.some((item) => this.hasValue(item, exact));
}
/**
* Returns whether the map contains all the given values
*
* @param values the values to check
* @param exact whether matching entry values exactly
*
* @returns whether the map contains all the given values
*
* @example
* ```ts
* map.hasAllValues(['red', 'green', 'blue'], ['top', 'right', 'bottom', 'left']);
*/
hasAllValues(values, exact = true) {
return this.isNotEmpty() && values.length > 0 && values.every((item) => this.hasValue(item, exact));
}
/**
* Returns whether the map is empty
*
* @returns whether the map is empty
*/
isEmpty() {
return !this.map.size;
}
/**
* Returns whether the map is not empty
*
* @returns whether the map is not empty
*/
isNotEmpty() {
return this.map.size > 0;
}
/**
* Returns the key with the given values
*
* @param values the values to inspect
* @param defaults the default key if nothing matches the given value
*
* @returns the key with the given value
*
* @example
* ```ts
* map.getKey(['foo', 'bar']);
* ```
*/
getKey(values, defaults) {
if (this.isEmpty()) {
return defaults;
}
for (const [k, vs] of this.entries()) {
if (values.length === vs.length && values.every((item) => vs.includes(item))) {
return k;
}
}
return defaults;
}
/**
* Response for returning the list of key/values to iterate
*
* @example
* ```ts
* for (const [key, values] of map) {
* console.log(key);
* }
* ```
*/
// @ts-ignore
[Symbol.iterator]() {
return this.map[Symbol.iterator]();
}
/**
* Returns the size of map
*
* @returns the size of map
*/
get size() {
return this.map.size;
}
/**
* Returns the string representation of the map identifier ('MultiValueMap')
*
* @returns the string representation of the map identifier
*/
get [Symbol.toStringTag]() {
return "MultiValueMap";
}
/**
* Returns the string representation of the map elements
*
* @returns the string representation of the map elements
*
* @example
* ```ts
* const map = MultiValueMap.of([
* ['color', ['red', 'green', 'blue']],
* ['position', ['top', 'right', 'bottom', 'left']]
* ]);
* console.log(map.toString()); // 'color:[red,green,blue];position:[top,right,bottom,left]'
*/
toString() {
return [...this].map((entry) => {
const [k, vs] = entry.length <= 1 ? [[], entry[0]] : [entry.slice(0, -1), entry[entry.length - 1]];
return `${k}:[${vs.join()}]`;
}).join(";");
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
MultiValueMap
});