UNPKG

als-statistics

Version:

Modular JS statistics toolkit for Node.js and the browser: descriptive stats, correlations (Pearson/Spearman/Kendall), t-tests & ANOVA (Student/Welch), reliability (Cronbach’s alpha), regression (linear/logistic), clustering (DBSCAN/HDBSCAN), and table/co

66 lines (55 loc) 2.9 kB
const isValid = (v) => typeof v === 'number' && Number.isFinite(v); const validator = v => { if (!isValid(v)) throw new Error(`value ${v} not valid`); return v } export class Cache { static key(name, ...parts) { return parts.length ? `${name}:${parts.join('|')}` : name; } #cache; #values; #emits = []; constructor(values) { this.#cache = new Map(); this.#values = Array.isArray(values) ? values : []; // стартовое состояние } onchange(fn) { if (typeof fn === 'function') this.#emits.push(fn) } /** Подписка на изменения values (и undo/redo). */ #emit(prevValues, meta) { for (const fn of this.#emits) fn(this, prevValues, meta); } get values() { return this.#values } set values(next) { /** Установка новых данных. Важно: сравнение по ссылке. Если меняешь массив «на месте», setter не сработает. Используй методы выше или передавай новый массив. */ if (Object.is(next, this.#values)) return; // та же ссылка — игнор const prev = this.#values; this.#values = next; this.#cache.clear(); // новая ссылка -> новый снимок данных => инвалидация кэша this.#emit(prev, { type: 'set' }); } get next() { return Array.from(this.#values); } // TODO this method not tested setAt(index, value) { /** Заменить элемент по индексу (если вышли за границы — ничего не делает). */ if (index < 0 || index >= this.#values.length) return this; const next = this.next; next[index] = validator(value); this.values = next; return this; } insertAt(index, ...items) { /** Вставить по индексу (или в конец, если индекс > длины). */ const next = this.next if(index === undefined || index >= next.length) return this.push(...items) next.splice(Math.max(0, index), 0, ...items.map(validator)); this.values = next return this; } removeAt(index, count = 1) { /** Удалить по индексу count элементов. */ if (index < 0 || index >= this.#values.length || count <= 0) return this; const next = this.next next.splice(index, count); this.values = next return this; } push(...items) { /** Добавить в конец. */ if (!items.length) return this; this.values = [...this.#values, ...items.map(validator)]; return this; } $(key, compute) { if (!this.#cache.has(key)) { const value = compute(this); this.#cache.set(key, value); } return this.#cache.get(key); } }