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
JavaScript
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);
}
}