Files
lik/ts/classes.objectmap.ts

287 lines
6.9 KiB
TypeScript

import * as plugins from './classes.plugins.js';
import { FastMap } from './classes.fastmap.js';
export const uni = (prefix: string = 'uni') => {
return `${prefix}xxxxxxxxxxx`.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0;
const v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
};
export interface IObjectmapForEachFunction<T> {
(itemArg: T): void;
}
export interface IObjectmapFindFunctionSync<T> {
(itemArg: T): boolean;
}
export interface IObjectmapFindFunction<T> {
(itemArg: T): Promise<boolean>;
}
export interface IObjectMapEventData<T> {
operation: 'add' | 'remove';
payload: T;
}
/**
* allows keeping track of objects
*/
export class ObjectMap<T> {
private fastMap = new FastMap<T>();
private reverseMap = new Map<T, string>();
// events
public eventSubject = new plugins.smartrx.rxjs.Subject<IObjectMapEventData<T>>();
/**
* returns a new instance
*/
constructor() {
// nothing here
}
/**
* the number of objects in the map
*/
public get length(): number {
return this.fastMap.size;
}
/**
* adds an object mapped to a string
* the string must be unique
*/
addMappedUnique(uniqueKeyArg: string, objectArg: T) {
this.fastMap.addToMap(uniqueKeyArg, objectArg);
this.reverseMap.set(objectArg, uniqueKeyArg);
}
/**
* fastest way to get an object from the map
* @param uniqueKey
*/
public getMappedUnique(uniqueKeyArg: string) {
return this.fastMap.getByKey(uniqueKeyArg);
}
/**
* remove key
* @param functionArg
*/
public removeMappedUnique(uniqueKey: string): T {
const object = this.fastMap.removeFromMap(uniqueKey);
if (object !== undefined) {
this.reverseMap.delete(object);
this.eventSubject.next({
operation: 'remove',
payload: object,
});
}
return object;
}
/**
* add object to Objectmap
* returns the key for the object (existing or new)
*/
public add(objectArg: T): string {
const existingKey = this.reverseMap.get(objectArg);
if (existingKey !== undefined) {
return existingKey;
}
const uniqueKey = uni('key');
this.addMappedUnique(uniqueKey, objectArg);
this.eventSubject.next({
operation: 'add',
payload: objectArg,
});
return uniqueKey;
}
/**
* like .add but adds an whole array of objects
*/
public addArray(objectArrayArg: T[]) {
for (const item of objectArrayArg) {
this.add(item);
}
}
/**
* check if object is in Objectmap
*/
public checkForObject(objectArg: T): boolean {
return this.reverseMap.has(objectArg);
}
/**
* get key for object
*/
public getKeyForObject(objectArg: T): string | null {
return this.reverseMap.get(objectArg) ?? null;
}
/**
* find object
*/
public async find(findFunction: IObjectmapFindFunction<T>): Promise<T> {
return this.fastMap.find(findFunction);
}
public findSync(findFunction: IObjectmapFindFunctionSync<T>): T {
for (const keyArg of this.fastMap.getKeys()) {
if (findFunction(this.fastMap.getByKey(keyArg))) {
return this.getMappedUnique(keyArg);
}
}
}
/**
* finds a specific element and then removes it
*/
public async findOneAndRemove(findFunction: IObjectmapFindFunction<T>): Promise<T> {
const foundElement = await this.find(findFunction);
if (foundElement) {
this.remove(foundElement);
}
return foundElement;
}
public findOneAndRemoveSync(findFunction: IObjectmapFindFunctionSync<T>): T {
const foundElement = this.findSync(findFunction);
if (foundElement) {
this.remove(foundElement);
}
return foundElement;
}
/**
* run function for each item in Objectmap
*/
public async forEach(functionArg: IObjectmapForEachFunction<T>) {
for (const keyArg of this.fastMap.getKeys()) {
await functionArg(this.fastMap.getByKey(keyArg));
}
}
/**
* gets an object in the Observablemap and removes it, so it can't be retrieved again
*/
public getOneAndRemove(): T {
const keys = this.fastMap.getKeys();
if (keys.length === 0) {
return null;
} else {
const keyToUse = keys[0];
const removedItem = this.fastMap.removeFromMap(keyToUse);
this.reverseMap.delete(removedItem);
this.eventSubject.next({
operation: 'remove',
payload: removedItem,
});
return removedItem;
}
}
/**
* returns a cloned array of all the objects currently in the Objectmap
*/
public getArray(): T[] {
return this.fastMap.values();
}
/**
* check if Objectmap ist empty
*/
public isEmpty(): boolean {
return this.fastMap.size === 0;
}
/**
* remove object from Objectmap
*/
public remove(objectArg: T): T {
const keyArg = this.reverseMap.get(objectArg);
if (keyArg !== undefined) {
const removedObject = this.fastMap.removeFromMap(keyArg);
this.reverseMap.delete(removedObject);
this.eventSubject.next({
operation: 'remove',
payload: removedObject,
});
return removedObject;
}
return null;
}
/**
* wipe Objectmap
*/
public wipe() {
const keys = this.fastMap.getKeys();
for (const keyArg of keys) {
const removedObject = this.fastMap.removeFromMap(keyArg);
this.reverseMap.delete(removedObject);
this.eventSubject.next({
operation: 'remove',
payload: removedObject,
});
}
}
/**
* returns a new Objectmap that includes
*/
public concat(objectMapArg: ObjectMap<T>) {
const concattedObjectMap = new ObjectMap<T>();
concattedObjectMap.fastMap.addAllFromOther(this.fastMap);
concattedObjectMap.fastMap.addAllFromOther(objectMapArg.fastMap);
// rebuild reverse map for the concatenated map
for (const key of concattedObjectMap.fastMap.getKeys()) {
concattedObjectMap.reverseMap.set(concattedObjectMap.fastMap.getByKey(key), key);
}
return concattedObjectMap;
}
/**
* tries to merge another Objectmap
* Note: uniqueKeyCollisions will cause overwrite
* @param objectMapArg
*/
public addAllFromOther(objectMapArg: ObjectMap<T>) {
this.fastMap.addAllFromOther(objectMapArg.fastMap);
// rebuild reverse map
for (const key of objectMapArg.fastMap.getKeys()) {
this.reverseMap.set(objectMapArg.fastMap.getByKey(key), key);
}
}
public map<U>(fn: (item: T) => U): U[] {
return this.getArray().map(fn);
}
public filter(fn: (item: T) => boolean): T[] {
return this.getArray().filter(fn);
}
public reduce<U>(fn: (acc: U, item: T) => U, initial: U): U {
return this.getArray().reduce(fn, initial);
}
public [Symbol.iterator](): Iterator<T> {
return this.getArray()[Symbol.iterator]();
}
/**
* destroys the ObjectMap, completing the eventSubject and clearing all entries
*/
public destroy() {
this.wipe();
this.reverseMap.clear();
this.eventSubject.complete();
}
}