[SE] refactor cache for exact mathces

This commit is contained in:
Aleksandr Nagaev
2025-10-30 13:23:53 +03:00
parent 4f46d82523
commit ea8e92092e

View File

@ -2903,6 +2903,174 @@ function (window, undefined) {
* @typedef {cNumber | cString | cBool | cError} LookUpElement
*/
function TypedMapCache() {
this.data = {};
}
TypedMapCache.prototype.getData = function (ws, rowCol, type, startIndex, endIndex, bHor) {
const wsId = ws.getId();
if (!this.data[wsId]) {
this.data[wsId] = {vertical: {}, horizontal: {}};
}
const axisData = bHor ? this.data[wsId].horizontal : this.data[wsId].vertical;
if (!axisData[rowCol]) {
axisData[rowCol] = {start: startIndex, end: endIndex, data: {}};
const c1 = bHor ? startIndex : rowCol;
const r1 = bHor ? rowCol : startIndex;
const c2 = bHor ? endIndex : rowCol;
const r2 = bHor ? rowCol : endIndex;
const fullRange = ws.getRange3(r1, c1, r2, c2);
fullRange._foreachNoEmpty(function (cell, r, c) {
const value = checkTypeCell(cell, true);
const index = bHor ? c : r;
if (!axisData[rowCol].data[value.type]) {
axisData[rowCol].data[value.type] = new Map();
}
const map = axisData[rowCol].data[value.type];
if (!map.has(value.value)) {
map.set(value.value, []);
}
const arr = axisData[rowCol].data[value.type].get(value.value);
arr.push(index);
});
axisData[rowCol].start = startIndex;
axisData[rowCol].end = endIndex;
} else {
if (startIndex < axisData[rowCol].start) {
const c1 = bHor ? startIndex : rowCol;
const r1 = bHor ? rowCol : startIndex;
const c2 = bHor ? axisData[rowCol].start : rowCol;
const r2 = bHor ? rowCol : axisData[rowCol].start;
const fullRange = ws.getRange3(r1, c1, r2, c2);
fullRange._foreachNoEmpty(function (cell, r, c) {
const value = checkTypeCell(cell, true);
const index = bHor ? c : r;
if (!axisData[rowCol].data[value.type]) {
axisData[rowCol].data[value.type] = new Map();
}
const map = axisData[rowCol].data[value.type];
if (!map.has(value.value)) {
map.set(value.value, []);
}
const arr = axisData[rowCol].data[value.type].get(value.value);
arr.unshift(index);
});
axisData[rowCol].start = startIndex;
}
if (endIndex > axisData[rowCol].end) {
const c1 = bHor ? axisData[rowCol].end : rowCol;
const r1 = bHor ? rowCol : axisData[rowCol].end;
const c2 = bHor ? endIndex : rowCol;
const r2 = bHor ? rowCol : endIndex;
const fullRange = ws.getRange3(r1, c1, r2, c2);
fullRange._foreachNoEmpty(function (cell, r, c) {
const value = checkTypeCell(cell, true);
const index = bHor ? c : r;
if (!axisData[rowCol].data[value.type]) {
axisData[rowCol].data[value.type] = new Map();
}
const map = axisData[rowCol].data[value.type];
if (!map.has(value.value)) {
map.set(value.value, []);
}
const arr = axisData[rowCol].data[value.type].get(value.value);
arr.push(index);
});
axisData[rowCol].end = endIndex;
}
}
return axisData[rowCol].data[type];
};
TypedMapCache.prototype.changeAxisData = function (cellRowOrCol, axisData, oldValue, oldType, newValue, newType, bHor) {
const staticIndex = bHor ? cellRowOrCol.nRow : cellRowOrCol.nCol;
const changedIndex = bHor ? cellRowOrCol.nCol : cellRowOrCol.nRow;
const colRowData = axisData[staticIndex];
if (colRowData && changedIndex >= colRowData.start && changedIndex <= colRowData.end) {
if (oldValue !== null) {
const mapOldType = colRowData.data[oldType];
if (mapOldType) {
if (mapOldType.has(oldValue)) {
const arr = mapOldType.get(oldValue);
if (arr.length === 1) {
mapOldType.delete(oldValue);
} else {
const newArr = [];
for (let i = 0; i < arr.length; i += 1) {
if (arr[i] !== changedIndex) {
newArr.push(arr[i]);
}
}
mapOldType.set(newArr);
}
}
}
}
if (newValue !== null) {
const mapNewType = colRowData.data[newType];
if (mapNewType) {
if (mapNewType.has(newValue)) {
const arr = mapNewType.get(newValue);
const newArr = [];
if (arr.length === 1) {
newArr.push(arr[0]);
changedIndex > arr[0] ? newArr.push(changedIndex) : newArr.unshift(changedIndex);
} else {
if (arr[0] < changedIndex) {
newArr.push(changedIndex);
}
for(let i = 1; i < arr.length; i += 1) {
if (arr[i] < changedIndex && arr[i - 1] > changedIndex) {
newArr.push(changedIndex);
}
newArr.push(arr[i]);
}
if (arr[arr.length - 1] < changedIndex) {
newArr.push(changedIndex);
}
}
mapNewType.set(newValue, newArr);
} else {
mapNewType.set(newValue, [changedIndex]);
}
} else {
colRowData.data[newType] = new Map();
colRowData.data[newType].set(newValue, [changedIndex])
}
}
}
};
TypedMapCache.prototype.changeData = function (cell, dataOld, dataNew) {
const wsId = cell.ws.getId();
const data = this.data[wsId];
if (data) {
const verticalData = data.vertical;
const horizontalData = data.horizontal;
let oldValue = null;
let oldType = null;
if (dataOld) {
const oldCellValue = dataOld && dataOld.value;
oldType = oldCellValue && oldCellValue.type;
oldValue = oldType === cElementType.number ? oldCellValue.number : oldCellValue.text;
if (oldValue && oldType === cElementType.string) {
oldValue = oldValue.toLowerCase();
}
}
let newValue = null;
let newType = null;
if (dataNew) {
newType = dataNew.type;
newValue = dataNew.value;
if (newValue && newType === cElementType.string) {
newValue = newValue.toLowerCase();
}
}
if (oldType === newType && newValue === oldValue) {
return;
}
this.changeAxisData(cell, verticalData, oldValue, oldType, newValue, newType, false);
this.changeAxisData(cell, horizontalData, oldValue, oldType, newValue, newType, true);
}
};
/**
* @constructor
*/
@ -2910,7 +3078,7 @@ function (window, undefined) {
this.cacheId = {};
this.cacheRanges = {};
this.bHor = bHor;
this.sortedCache = {data: {}};
this.sortedCache = new TypedMapCache();
this.typedCache = new TypedCache();
this.typedCacheValuesMap = new TypedCache();
}
@ -3627,80 +3795,7 @@ function (window, undefined) {
* @return {Uint32Array} Sorted array of worksheet indices for the specified data type
*/
VHLOOKUPCache.prototype._getSortedCache = function(ws, rowCol, type, startIndex, endIndex, bHor) {
const wsId = ws.Get_Id();
const sortedCacheData = this.sortedCache.data;
if (!sortedCacheData[wsId]) {
sortedCacheData[wsId] = {horizontal: {}, vertical: {}};
}
/** @type {TypedCacheAxis} */
const axisData = bHor ? sortedCacheData[wsId].horizontal : sortedCacheData[wsId].vertical;
if (!axisData[rowCol]) {
axisData[rowCol] = {start: startIndex, end: endIndex, data: {}};
const c1 = bHor ? startIndex : rowCol;
const r1 = bHor ? rowCol : startIndex;
const c2 = bHor ? endIndex : rowCol;
const r2 = bHor ? rowCol : endIndex;
const fullRange = ws.getRange3(r1, c1, r2, c2);
fullRange._foreachNoEmpty(function (cell, r, c) {
const value = checkTypeCell(cell, true);
const index = bHor ? c : r;
if (!axisData[rowCol].data[value.type]) {
axisData[rowCol].data[value.type] = new Map();
}
const map = axisData[rowCol].data[value.type];
if (!map.has(value.value)) {
map.set(value.value, []);
}
const arr = axisData[rowCol].data[value.type].get(value.value);
arr.push(index);
});
axisData[rowCol].start = startIndex;
axisData[rowCol].end = endIndex;
} else {
if (startIndex < axisData[rowCol].start) {
const c1 = bHor ? startIndex : rowCol;
const r1 = bHor ? rowCol : startIndex;
const c2 = bHor ? axisData[rowCol].start : rowCol;
const r2 = bHor ? rowCol : axisData[rowCol].start;
const fullRange = ws.getRange3(r1, c1, r2, c2);
fullRange._foreachNoEmpty(function (cell, r, c) {
const value = checkTypeCell(cell, true);
const index = bHor ? c : r;
if (!axisData[rowCol].data[value.type]) {
axisData[rowCol].data[value.type] = new Map();
}
const map = axisData[rowCol].data[value.type];
if (!map.has(value.value)) {
map.set(value.value, []);
}
const arr = axisData[rowCol].data[value.type].get(value.value);
arr.unshift(index);
});
axisData[rowCol].start = startIndex;
}
if (endIndex > axisData[rowCol].end) {
const c1 = bHor ? axisData[rowCol].end : rowCol;
const r1 = bHor ? rowCol : axisData[rowCol].end;
const c2 = bHor ? endIndex : rowCol;
const r2 = bHor ? rowCol : endIndex;
const fullRange = ws.getRange3(r1, c1, r2, c2);
fullRange._foreachNoEmpty(function (cell, r, c) {
const value = checkTypeCell(cell, true);
const index = bHor ? c : r;
if (!axisData[rowCol].data[value.type]) {
axisData[rowCol].data[value.type] = new Map();
}
const map = axisData[rowCol].data[value.type];
if (!map.has(value.value)) {
map.set(value.value, []);
}
const arr = axisData[rowCol].data[value.type].get(value.value);
arr.push(index);
});
axisData[rowCol].end = endIndex;
}
}
return axisData[rowCol].data[type];
return this.sortedCache.getData(ws, rowCol, type, startIndex, endIndex, bHor);
};
/**
* Retrieves or generates a typed cache containing worksheet indices for approximate match lookups.
@ -3815,79 +3910,12 @@ function (window, undefined) {
elem.data.results = {};
}
}
const sortedCacheData = this.sortedCache.data
const data = sortedCacheData[wsId];
if (data) {
const verticalData = data.vertical;
const horizontalData = data.horizontal;
const colData = verticalData[cell.nCol];
if (colData && cell.nRow >= colData.start && cell.nRow <= colData.end) {
if (dataOld) {
const oldCellValue = dataOld.value;
const oldType = oldCellValue.type;
if (colData.data[oldType]) {
let oldValue = oldType === cElementType.number ? oldCellValue.number : oldCellValue.text;
if (oldType.type === cElementType.string) {
oldValue = oldValue.toLowerCase();
}
const mapOldType = colData.data[oldType];
if (mapOldType.has(oldValue)) {
const arr = mapOldType.get(oldValue);
if (arr.length === 1) {
mapOldType.delete(oldValue);
} else {
const newArr = [];
for (let i = 0; i < arr.length; i += 1) {
if (arr[i] !== cell.nRow) {
newArr.push(arr[i]);
}
}
}
}
}
}
const newType = dataNew.type;
let newValue = dataNew.value;
if (newType === cElementType.string) {
newValue = newValue.toLowerCase();
}
const mapNewType = colData.data[newType];
if (mapNewType) {
if (mapNewType.has(newValue)) {
const arr = mapNewType.get(newValue);
const newArr = [];
if (arr.length === 1) {
newArr.push(arr[0]);
cell.nRow > arr[0] ? newArr.push(cell.nRow) : newArr.unshift(cell.nRow);
} else {
if (arr[0] < cell.nRow) {
newArr.push(cell.nRow);
}
for(let i = 1; i < arr.length; i += 1) {
if (arr[i] < cell.nRow && arr[i - 1] > cell.nRow) {
newArr.push(cell.nRow);
}
newArr.push(arr[i]);
}
if (arr[arr.length - 1] < cell.nRow) {
newArr.push(cell.nRow);
}
}
mapNewType.set(newValue, newArr);
} else {
mapNewType.set(newValue, [cell.nRow]);
}
} else {
colData.data[newType] = new Map();
colData.data[newType].set(newValue, [cell.nRow])
}
}
}
this.sortedCache.changeData(cell, dataOld, dataNew);
};
VHLOOKUPCache.prototype.clean = function () {
this.cacheId = {};
this.cacheRanges = {};
this.sortedCache = {data: {}};
this.sortedCache = new TypedMapCache();
this.typedCache.clean();
this.typedCacheValuesMap.clean();
};