import hasOwnProperty from './hasOwnProperty.js'; const NULL = {}; export interface FastMap { size: number; empty: number; object: Record; has(key: string): boolean; get(key: string): T | undefined; set(key: string, value: T): FastMap; delete(key: string): FastMap; clear(): void; /** Use the test */ test(): ((value: T) => boolean) | undefined; /** Set test function */ test(fn: (value: T) => boolean): FastMap | undefined; clean(): void; } export default function fastmap( input?: Record ): FastMap { let obj: Record = {}, test: ((value: T) => boolean) | undefined; function has(key: string): boolean { return hasOwnProperty(obj, key) && obj[key] !== NULL; } const map: FastMap = { size: 0, empty: 0, object: obj, has: has, get(key: string): T | undefined { return has(key) ? (obj[key] as T) : undefined; }, set(key: string, value: T): FastMap { if (!has(key)) { ++map.size; if (obj[key] === NULL) --map.empty; } obj[key] = value; return this; }, delete(key: string): FastMap { if (has(key)) { --map.size; ++map.empty; obj[key] = NULL; } return this; }, clear(): void { map.size = map.empty = 0; map.object = obj = {}; }, /** See the FastMap interface for the expected return type, which depends on whether a function is provided */ test(fn?: (value: T) => boolean): any { if (arguments.length) { test = fn; return map; } else { return test; } }, clean(): void { const next: Record = {}; let size = 0; for (const key in obj) { const value = obj[key]; if (value !== NULL && (!test || !test(value as T))) { next[key] = value; ++size; } } map.size = size; map.empty = 0; map.object = obj = next; } }; if (input) Object.keys(input).forEach((key) => { map.set(key, input[key]); }); return map; }