UNPKG

rotated-array-set

Version:
103 lines (102 loc) 2.98 kB
export function arrayEqual(a, b) { if (a.length !== b.length) return false; else if (a.length === 0) return true; return !a.some((nodeA, i) => nodeA !== b[i]); } export function rotatedArrayEqual(a, b) { if (a.length !== b.length) return false; else if (!a.length) return true; const offset = b.indexOf(a[0]); if (offset === -1) return false; const _b = offset === 0 ? b : [...b.slice(offset), ...b.slice(0, offset)]; return arrayEqual(a, _b); } function simpleHash(text, hashCache) { const textLength = text.length; if (textLength === 0) return 4711; const quads = []; for (let i = 0; i < textLength; ++i) { let byte = text.charCodeAt(i) + i * 13; byte = byte % 256; const mod = i % 4; if (mod === 0) quads.push(byte); else quads[quads.length - 1] |= byte << (mod === 1 ? 8 : mod === 2 ? 16 : 24); } const hash = quads.reduce((prev, cur) => prev ^ cur, textLength * 13); hashCache.set(text, hash); return hash; } function nodeHash(keys, hashCache) { if (keys.length === 0) return 31415; return keys .map(key => { var _a; return (_a = hashCache.get(key)) !== null && _a !== void 0 ? _a : simpleHash(key, hashCache); }) .reduce((prev, cur) => prev ^ cur, keys.length * 13); } export class RotatedArraySet { constructor(stringify = (t) => `${t}`) { this.stringify = stringify; this.hashCache = new Map(); this.tree = new Map(); } makeNode(arr) { const keys = arr.map(t => this.stringify(t)); const hash = nodeHash(keys, this.hashCache); const node = { keys, hash, value: arr, }; return node; } add(arr) { const node = this.makeNode(arr); if (this._has(node)) return false; let set = this.tree.get(node.hash); if (!set) { set = new Set(); this.tree.set(node.hash, set); } set.add(node); return true; } _has(node) { const set = this.tree.get(node.hash); if (!set) return undefined; for (const iter of set.values()) if (rotatedArrayEqual(iter.keys, node.keys)) return iter; /* istanbul ignore next */ return undefined; } has(arr) { return !!this._has(this.makeNode(arr)); } delete(arr) { const node = this._has(this.makeNode(arr)); if (!node) return false; const set = this.tree.get(node.hash); set.delete(node); if (set.size === 0) this.tree.delete(node.hash); return true; } values() { const set = new Set([...this.tree.values()].flatMap(set => [...set.values()])); return [...set].map(({ value }) => value); } }