@adaptabletools/adaptable
Version:
Powerful data-agnostic HTML5 AG Grid extension which provides advanced, cutting-edge functionality to meet all DataGrid requirements
279 lines (278 loc) • 8.8 kB
JavaScript
const SORT_ASC_REVISION = (p1, p2) => sortAscending(p1.revision, p2.revision);
export class DeepMap {
static clone(map) {
const clone = new DeepMap();
map.visit((pair, keys) => {
clone.set(keys, pair.value);
});
return clone;
}
constructor(initial) {
this.map = new Map();
this.length = 0;
this.revision = 0;
this.visit = (fn) => {
this.map.forEach((_, k) => this.visitKey(k, this.map, [], fn));
};
this.visitDepthFirst = (fn) => {
this.visitWithNext([], fn);
};
this.visitWithNext = (parentKeys, fn, currentMap = this.map) => {
if (!currentMap) {
return;
}
let i = 0;
currentMap.forEach((_, key) => {
const pair = currentMap.get(key);
if (!pair) {
return;
}
const { map } = pair;
const keys = [...parentKeys, key];
const next = map ? () => this.visitWithNext(keys, fn, map) : undefined;
if (pair.hasOwnProperty('value')) {
fn(pair.value, keys, i, next);
i++;
}
else {
next?.();
}
});
};
if (initial) {
initial.forEach((entry) => {
const [keys, value] = entry;
this.set(keys, value);
});
}
}
set(keys, value) {
let currentMap = this.map;
if (!keys.length) {
throw `Cannot set given keys - please provide a non-empty keys array.`;
// currentMap.set((EMPTY_KEY as unknown) as K, { value });
// this.length++;
// return this;
}
for (let i = 0, len = keys.length; i < len; i++) {
const key = keys[i];
const last = i === len - 1;
if (last) {
const pair = currentMap.has(key) ? currentMap.get(key) : {};
pair.revision = this.revision++;
pair.value = value;
currentMap.set(key, pair);
this.length++;
}
else {
const pair = currentMap.has(key) ? currentMap.get(key) : {};
if (!pair.map) {
pair.map = new Map();
currentMap.set(key, pair);
}
currentMap = pair.map;
}
}
return this;
}
get(keys) {
let currentMap = this.map;
if (!keys.length) {
throw `Cannot get given keys - please provide a non-empty keys array.`;
// return currentMap.get((EMPTY_KEY as unknown) as K)?.value;
}
for (let i = 0, len = keys.length; i < len; i++) {
const key = keys[i];
const last = i === len - 1;
if (last) {
return currentMap.get(key)?.value;
}
else {
const pair = currentMap.get(key);
if (!pair || !pair.map) {
return;
}
currentMap = pair.map;
}
}
return;
}
get size() {
return this.length;
}
clear() {
const clearMap = (map) => {
map.forEach((value, _key) => {
const { map } = value;
if (map) {
clearMap(map);
}
});
map.clear();
};
clearMap(this.map);
this.length = 0;
this.revision = 0;
}
delete(keys) {
keys = [...keys];
let currentMap = this.map;
if (!keys.length) {
throw `Cannot delete given keys - please provide a non-empty keys array.`;
// if (currentMap.has(EMPTY_KEY as unknown as K)) {
// this.length--;
// }
// return currentMap.delete((EMPTY_KEY as unknown) as K);
}
let maps = [currentMap];
let result = false;
for (let i = 0, len = keys.length; i < len; i++) {
const key = keys[i];
const last = i === len - 1;
if (last) {
const pair = currentMap.get(key);
if (pair) {
if (pair.hasOwnProperty('value')) {
delete pair.value;
delete pair.revision;
result = true;
this.length--;
}
if (pair.map && pair.map.size === 0) {
delete pair.map;
}
if (!pair.map) {
// pair is empty, so we can remove it altogether
currentMap.delete(key);
}
}
break;
}
else {
const pair = currentMap.get(key);
if (!pair || !pair.map) {
result = false;
break;
}
currentMap = pair.map;
maps.push(currentMap);
}
}
while (maps.length) {
let map = maps.pop();
let key = keys.pop();
if (key && map?.size === 0) {
let parentMap = maps[maps.length - 1];
let pair = parentMap?.get(key);
if (pair) {
// pair.map === map ; which can be deleted
delete pair.map;
if (!pair.hasOwnProperty('value')) {
// whole pair can be successfully deleted from parentMap
parentMap.delete(key);
}
}
}
}
return result;
}
has(keys) {
let currentMap = this.map;
if (!keys.length) {
throw `Cannot find existing given keys - please provide a non-empty keys array.`;
// return (
// currentMap.has((EMPTY_KEY as unknown) as K) &&
// currentMap.get((EMPTY_KEY as unknown) as K)?.value !== undefined
// );
}
for (let i = 0, len = keys.length; i < len; i++) {
const key = keys[i];
const last = i === len - 1;
if (last) {
const pair = currentMap.get(key);
return !!pair && pair.hasOwnProperty('value');
}
else {
const pair = currentMap.get(key);
if (!pair || !pair.map) {
return false;
}
currentMap = pair.map;
}
}
return false;
}
visitKey(key, currentMap, parentKeys, fn) {
const pair = currentMap.get(key);
if (!pair) {
return;
}
const { map } = pair;
const keys = [...parentKeys, key];
const next = once(() => {
if (map) {
map.forEach((_, k) => {
this.visitKey(k, map, keys, fn);
});
}
});
if (pair.hasOwnProperty('value')) {
fn(pair, keys, next);
}
// if it was called by fn, it won't be called again, as it's once-d
next();
}
getArray(fn) {
const result = [];
this.visit((pair, keys) => {
result.push(fn({ ...pair, keys }));
});
return result;
}
values() {
return this.sortedIterator((pair) => pair.value);
}
keys() {
const keys = this.sortedIterator((pair) => pair.keys);
return keys;
}
entries() {
return this.sortedIterator((pair) => [pair.keys, pair.value]);
}
topDownEntries() {
return this.getArray((pair) => [pair.keys, pair.value]);
}
topDownKeys() {
return this.getArray((pair) => pair.keys);
}
topDownValues() {
return this.getArray((pair) => pair.value);
}
sortedIterator(fn) {
const result = [];
this.visit((pair, keys) => {
result.push({ ...pair, keys });
});
result.sort(SORT_ASC_REVISION);
function* makeIterator() {
for (let i = 0, len = result.length; i < len; i++) {
yield fn(result[i]);
}
}
return makeIterator();
}
}
export function once(fn) {
let called = false;
let result = null;
const onceFn = () => {
if (called) {
return result;
}
called = true;
result = fn();
return result;
};
return onceFn;
}
const sortAscending = (a, b) => a - b;