fixed: formula optimization bugfixes, renaming variables, defining types

This commit is contained in:
Shashank Agarwal 2024-12-16 06:36:47 +00:00
parent d36e50e72b
commit 12c2c0c2f1
14 changed files with 209 additions and 121 deletions

View File

@ -19,7 +19,7 @@ import { jfrefreshgrid } from "../modules/refresh";
import { setRowHeight } from "../api"; import { setRowHeight } from "../api";
import { CFSplitRange } from "../modules"; import { CFSplitRange } from "../modules";
import clipboard from "../modules/clipboard"; import clipboard from "../modules/clipboard";
import { setFormulaObject } from "../modules/formulaHelper"; import { setFormulaCellInfo } from "../modules/formulaHelper";
function postPasteCut( function postPasteCut(
ctx: Context, ctx: Context,
@ -33,7 +33,7 @@ function postPasteCut(
// clearTimeout(refreshCanvasTimeOut); // clearTimeout(refreshCanvasTimeOut);
for (let r = source.range.row[0]; r <= source.range.row[1]; r += 1) { for (let r = source.range.row[0]; r <= source.range.row[1]; r += 1) {
for (let c = source.range.column[0]; c <= source.range.column[1]; c += 1) { for (let c = source.range.column[0]; c <= source.range.column[1]; c += 1) {
setFormulaObject(ctx, { r, c, id: source.sheetId }); setFormulaCellInfo(ctx, { r, c, id: source.sheetId });
if (`${r}_${c}_${source.sheetId}` in execF_rc) { if (`${r}_${c}_${source.sheetId}` in execF_rc) {
continue; continue;
} }
@ -45,7 +45,7 @@ function postPasteCut(
for (let r = target.range.row[0]; r <= target.range.row[1]; r += 1) { for (let r = target.range.row[0]; r <= target.range.row[1]; r += 1) {
for (let c = target.range.column[0]; c <= target.range.column[1]; c += 1) { for (let c = target.range.column[0]; c <= target.range.column[1]; c += 1) {
setFormulaObject(ctx, { r, c, id: source.sheetId }); setFormulaCellInfo(ctx, { r, c, id: source.sheetId });
if (`${r}_${c}_${target.sheetId}` in execF_rc) { if (`${r}_${c}_${target.sheetId}` in execF_rc) {
continue; continue;
} }

View File

@ -63,17 +63,17 @@ export function setConditionRules(
const rangeArr = getRangeByTxt(ctx, v); const rangeArr = getRangeByTxt(ctx, v);
// 判断条件值是不是选区 // 判断条件值是不是选区
if (rangeArr.length > 1) { if (rangeArr.length > 1) {
const r1 = rangeArr[0].row[0]; const r1 = rangeArr?.[0]?.row[0];
const r2 = rangeArr[0].row[1]; const r2 = rangeArr?.[0]?.row[1];
const c1 = rangeArr[0].column[0]; const c1 = rangeArr?.[0]?.column[0];
const c2 = rangeArr[0].column[1]; const c2 = rangeArr?.[0]?.column[1];
if (r1 === r2 && c1 === c2) { if (r1 === r2 && c1 === c2) {
const d = getFlowdata(ctx); const d = getFlowdata(ctx);
if (!d) return; if (!d || _.isNil(r1) || _.isNil(c1)) return;
v = getCellValue(r1, c1, d); v = getCellValue(r1, c1, d);
conditionRange.push({ conditionRange.push({
row: rangeArr[0].row, row: rangeArr?.[0]?.row,
column: rangeArr[0].column, column: rangeArr?.[0]?.column,
}); });
conditionValue.push(v); conditionValue.push(v);
} else { } else {
@ -97,17 +97,17 @@ export function setConditionRules(
return; return;
} }
if (rangeArr1.length === 1) { if (rangeArr1.length === 1) {
const r1 = rangeArr1[0].row[0]; const r1 = rangeArr1?.[0]?.row[0];
const r2 = rangeArr1[0].row[1]; const r2 = rangeArr1?.[0]?.row[1];
const c1 = rangeArr1[0].column[0]; const c1 = rangeArr1?.[0]?.column[0];
const c2 = rangeArr1[0].column[1]; const c2 = rangeArr1?.[0]?.column[1];
if (r1 === r2 && c1 === c2) { if (r1 === r2 && c1 === c2) {
const d = getFlowdata(ctx); const d = getFlowdata(ctx);
if (!d) return; if (!d || _.isNil(r1) || _.isNil(c1)) return;
v1 = getCellValue(r1, c1, d); v1 = getCellValue(r1, c1, d);
conditionRange.push({ conditionRange.push({
row: rangeArr1[0].row, row: rangeArr1?.[0]?.row,
column: rangeArr1[0].column, column: rangeArr1?.[0]?.column,
}); });
conditionValue.push(v1); conditionValue.push(v1);
} else { } else {
@ -127,17 +127,17 @@ export function setConditionRules(
return; return;
} }
if (rangeArr2.length === 1) { if (rangeArr2.length === 1) {
const r1 = rangeArr2[0].row[0]; const r1 = rangeArr2?.[0]?.row[0];
const r2 = rangeArr2[0].row[1]; const r2 = rangeArr2?.[0]?.row[1];
const c1 = rangeArr2[0].column[0]; const c1 = rangeArr2?.[0]?.column[0];
const c2 = rangeArr2[0].column[1]; const c2 = rangeArr2?.[0]?.column[1];
if (r1 === r2 && c1 === c2) { if (r1 === r2 && c1 === c2) {
const d = getFlowdata(ctx); const d = getFlowdata(ctx);
if (!d) return; if (!d || _.isNil(r1) || _.isNil(c1)) return;
v2 = getCellValue(r1, c1, d); v2 = getCellValue(r1, c1, d);
conditionRange.push({ conditionRange.push({
row: rangeArr2[0].row, row: rangeArr2?.[0]?.row,
column: rangeArr2[0].column, column: rangeArr2?.[0]?.column,
}); });
} else { } else {
ctx.warnDialog = conditionformat.onlySingleCell; ctx.warnDialog = conditionformat.onlySingleCell;

View File

@ -1,6 +1,13 @@
import _ from "lodash"; import _ from "lodash";
import { Context, getFlowdata } from "../context"; import { Context, getFlowdata } from "../context";
import { Cell, CellMatrix, Range, Selection, SingleRange } from "../types"; import {
Cell,
CellMatrix,
FormulaDependency,
Range,
Selection,
SingleRange,
} from "../types";
import { getSheetIndex, indexToColumnChar, rgbToHex } from "../utils"; import { getSheetIndex, indexToColumnChar, rgbToHex } from "../utils";
import { checkCF, getComputeMap } from "./ConditionFormat"; import { checkCF, getComputeMap } from "./ConditionFormat";
import { getFailureText, validateCellData } from "./dataVerification"; import { getFailureText, validateCellData } from "./dataVerification";
@ -21,7 +28,7 @@ import {
} from "./inline-string"; } from "./inline-string";
import { isRealNull, isRealNum, valueIsError } from "./validation"; import { isRealNull, isRealNum, valueIsError } from "./validation";
import { getCellTextInfo } from "./text"; import { getCellTextInfo } from "./text";
import { setFormulaObject } from "./formulaHelper"; import { setFormulaCellInfo } from "./formulaHelper";
// TODO put these in context ref // TODO put these in context ref
// let rangestart = false; // let rangestart = false;
@ -1082,7 +1089,7 @@ export function updateCell(
}); });
} }
setFormulaObject(ctx, { r, c, id: ctx.currentSheetId }); setFormulaCellInfo(ctx, { r, c, id: ctx.currentSheetId });
ctx.formulaCache.execFunctionGlobalData = null; ctx.formulaCache.execFunctionGlobalData = null;
} }
@ -1210,7 +1217,7 @@ export function getRangetxt(
// 把string A1:A2转为选区数组 // 把string A1:A2转为选区数组
export function getRangeByTxt(ctx: Context, txt: string) { export function getRangeByTxt(ctx: Context, txt: string) {
let range = []; let range: (FormulaDependency | null)[] = [];
if (txt.indexOf(",") !== -1) { if (txt.indexOf(",") !== -1) {
const arr = txt.split(","); const arr = txt.split(",");
for (let i = 0; i < arr.length; i += 1) { for (let i = 0; i < arr.length; i += 1) {

View File

@ -1,3 +1,4 @@
import _ from "lodash";
import { import {
colLocationByIndex, colLocationByIndex,
Context, Context,
@ -39,9 +40,9 @@ export function dataRangeSelection(
ctx.luckysheetCellUpdate = [row_index, col_index]; ctx.luckysheetCellUpdate = [row_index, col_index];
const range = getRangeByTxt(ctx, rangT); const range = getRangeByTxt(ctx, rangT);
const r = range[0].row; const r = range?.[0]?.row;
const c = range[0].column; const c = range?.[0]?.column;
if (_.isNil(r) || _.isNil(c)) return;
const row_pre = rowLocationByIndex(r[0], ctx.visibledatarow)[0]; const row_pre = rowLocationByIndex(r[0], ctx.visibledatarow)[0];
const row = rowLocationByIndex(r[1], ctx.visibledatarow)[1]; const row = rowLocationByIndex(r[1], ctx.visibledatarow)[1];
const col_pre = colLocationByIndex(c[0], ctx.visibledatacolumn)[0]; const col_pre = colLocationByIndex(c[0], ctx.visibledatacolumn)[0];
@ -69,9 +70,12 @@ export function getDropdownList(ctx: Context, txt: string) {
const list: (string | number | boolean)[] = []; const list: (string | number | boolean)[] = [];
if (iscelldata(txt)) { if (iscelldata(txt)) {
const range = getcellrange(ctx, txt); const range = getcellrange(ctx, txt);
const index = getSheetIndex(ctx, range.sheetId) as number; const index = getSheetIndex(
ctx,
range?.sheetId || ctx.currentSheetId
) as number;
const d = ctx.luckysheetfile[index].data; const d = ctx.luckysheetfile[index].data;
if (!d) return []; if (!d || !range) return [];
for (let r = range.row[0]; r <= range.row[1]; r += 1) { for (let r = range.row[0]; r <= range.row[1]; r += 1) {
for (let c = range.column[0]; c <= range.column[1]; c += 1) { for (let c = range.column[0]; c <= range.column[1]; c += 1) {
if (!d[r]) { if (!d[r]) {
@ -854,12 +858,13 @@ export function confirmMessage(
ctx.warnDialog = generalDialog.noSeletionError; ctx.warnDialog = generalDialog.noSeletionError;
return false; return false;
} }
let str = range[range.length - 1].row[0]; let str = range?.[range.length - 1]?.row[0];
let edr = range[range.length - 1].row[1]; let edr = range?.[range.length - 1]?.row[1];
let stc = range[range.length - 1].column[0]; let stc = range?.[range.length - 1]?.column[0];
let edc = range[range.length - 1].column[1]; let edc = range?.[range.length - 1]?.column[1];
const d = getFlowdata(ctx); const d = getFlowdata(ctx);
if (!d) return false; if (!d || _.isNil(str) || _.isNil(edr) || _.isNil(stc) || _.isNil(edc))
return false;
if (str < 0) { if (str < 0) {
str = 0; str = 0;
} }

View File

@ -1,7 +1,17 @@
import _ from "lodash"; import _ from "lodash";
// @ts-ignore // @ts-ignore
import { Parser, ERROR_REF } from "@fortune-sheet/formula-parser"; import { Parser, ERROR_REF } from "@fortune-sheet/formula-parser";
import type { Cell, CellMatrix, History, Rect, Selection } from "../types"; import type {
Cell,
CellMatrix,
FormulaDependency,
FormulaDependenciesMap,
FormulaCell,
FormulaCellInfoMap,
History,
Rect,
Selection,
} from "../types";
import { Context, getFlowdata } from "../context"; import { Context, getFlowdata } from "../context";
import { import {
columnCharToIndex, columnCharToIndex,
@ -21,7 +31,7 @@ import { cancelFunctionrangeSelected, seletedHighlistByindex } from ".";
import { import {
arrayMatch, arrayMatch,
executeAffectedFormulas, executeAffectedFormulas,
setFormulaObject, setFormulaCellInfo,
getFormulaRunList, getFormulaRunList,
} from "./formulaHelper"; } from "./formulaHelper";
@ -98,9 +108,9 @@ export class FormulaCache {
execFunctionGlobalData: any; execFunctionGlobalData: any;
// useful in cut-paste operation where several cells may be affected but the formulas remains the same // useful in cut-paste operation where several cells may be affected but the formulas remains the same
formulaArrayCache: any; formulaDependenciesMap: FormulaDependenciesMap;
formulaObjects: any; formulaCellInfoMap: FormulaCellInfoMap | null;
constructor() { constructor() {
const that = this; const that = this;
@ -108,8 +118,8 @@ export class FormulaCache {
this.selectingRangeIndex = -1; this.selectingRangeIndex = -1;
this.functionlistMap = {}; this.functionlistMap = {};
this.execFunctionGlobalData = {}; this.execFunctionGlobalData = {};
this.formulaArrayCache = {}; this.formulaDependenciesMap = {};
this.formulaObjects = null; this.formulaCellInfoMap = null;
this.cellTextToIndexList = {}; this.cellTextToIndexList = {};
this.parser = new Parser(); this.parser = new Parser();
this.parser.on( this.parser.on(
@ -191,7 +201,7 @@ export class FormulaCache {
function requestUpdate(value: any) { function requestUpdate(value: any) {
if (value instanceof Object) { if (value instanceof Object) {
if (value.r !== undefined && value.r !== undefined) { if (value.r !== undefined && value.r !== undefined) {
setFormulaObject( setFormulaCellInfo(
ctx, ctx,
{ r: value.r, c: value.c, id: ctx.currentSheetId }, { r: value.r, c: value.c, id: ctx.currentSheetId },
data data
@ -302,7 +312,7 @@ export function getcellrange(
txt: string, txt: string,
formulaId?: string, formulaId?: string,
data?: CellMatrix data?: CellMatrix
) { ): FormulaDependency | null {
if (_.isNil(txt) || txt.length === 0) { if (_.isNil(txt) || txt.length === 0) {
return null; return null;
} }
@ -310,7 +320,7 @@ export function getcellrange(
let sheettxt = ""; let sheettxt = "";
let rangetxt = ""; let rangetxt = "";
let sheetId = null; let sheetId;
let sheetdata = null; let sheetdata = null;
const { luckysheetfile } = ctx; const { luckysheetfile } = ctx;
@ -369,7 +379,7 @@ export function getcellrange(
const col = columnCharToIndex(rangetxt.replace(/[^A-Za-z]/g, "")); const col = columnCharToIndex(rangetxt.replace(/[^A-Za-z]/g, ""));
if (!Number.isNaN(row) && !Number.isNaN(col)) { if (!Number.isNaN(row) && !Number.isNaN(col)) {
const item = { const item: FormulaDependency = {
row: [row, row], row: [row, row],
column: [col, col], column: [col, col],
sheetId, sheetId,
@ -380,8 +390,8 @@ export function getcellrange(
return null; return null;
} }
const rangetxtArr = rangetxt.split(":"); const rangetxtArr = rangetxt.split(":");
const row = []; const row: [number, number] = [-1, -1];
const col = []; const col: [number, number] = [-1, -1];
row[0] = parseInt(rangetxtArr[0].replace(/[^0-9]/g, ""), 10) - 1; row[0] = parseInt(rangetxtArr[0].replace(/[^0-9]/g, ""), 10) - 1;
row[1] = parseInt(rangetxtArr[1].replace(/[^0-9]/g, ""), 10) - 1; row[1] = parseInt(rangetxtArr[1].replace(/[^0-9]/g, ""), 10) - 1;
if (Number.isNaN(row[0])) { if (Number.isNaN(row[0])) {
@ -405,7 +415,7 @@ export function getcellrange(
return null; return null;
} }
const item = { const item: FormulaDependency = {
row, row,
column: col, column: col,
sheetId, sheetId,
@ -832,25 +842,11 @@ export function isFunctionRange(
export function getAllFunctionGroup(ctx: Context) { export function getAllFunctionGroup(ctx: Context) {
const { luckysheetfile } = ctx; const { luckysheetfile } = ctx;
let ret: any[] = []; let ret: FormulaCell[] = [];
for (let i = 0; i < luckysheetfile.length; i += 1) { for (let i = 0; i < luckysheetfile.length; i += 1) {
const file = luckysheetfile[i]; const file = luckysheetfile[i];
let { calcChain } = file; let { calcChain } = file;
/* 备注再次加载表格获取的数据可能是JSON字符串格式(需要进行发序列化处理) */
// if (calcChain) {
// const tempCalcChain: any[] = [];
// calcChain.forEach((item) => {
// if (typeof item === "string") {
// tempCalcChain.push(JSON.parse(item));
// } else {
// tempCalcChain.push(item);
// }
// });
// calcChain = tempCalcChain;
// file.calcChain = tempCalcChain;
// }
let { dynamicArray_compute } = file; let { dynamicArray_compute } = file;
if (_.isNil(calcChain)) { if (_.isNil(calcChain)) {
calcChain = []; calcChain = [];
@ -1187,14 +1183,14 @@ export function groupValuesRefresh(ctx: Context) {
} }
} }
function setFormulaObjectsCache( export function setFormulaCellInfoMap(
ctx: Context, ctx: Context,
calcChains: any, calcChains: any,
data: CellMatrix data?: CellMatrix
) { ) {
for (let i = 0; i < calcChains.length; i += 1) { for (let i = 0; i < calcChains.length; i += 1) {
const formulaCell = calcChains[i]; const formulaCell = calcChains[i];
setFormulaObject(ctx, formulaCell, data); setFormulaCellInfo(ctx, formulaCell, data);
} }
} }
@ -1232,7 +1228,7 @@ export function execFunctionGroup(
} }
// 1. get list of all functions in the sheet // 1. get list of all functions in the sheet
const calcChains = getAllFunctionGroup(ctx); // { "r": r, "c": c, "id": id, "func": func} const calcChains: FormulaCell[] = getAllFunctionGroup(ctx);
// 2. Store the cells involved in the modification // 2. Store the cells involved in the modification
const updateValueObjects: any = {}; const updateValueObjects: any = {};
@ -1247,30 +1243,33 @@ export function execFunctionGroup(
} }
} }
// 3. formulaObjects: a cache of ALL formulas vs their ranges // 3. formulaCellInfoMap: a cache of ALL formulas vs their ranges
if (!ctx.formulaCache.formulaObjects) { if (
ctx.formulaCache.formulaObjects = {}; !ctx.formulaCache.formulaCellInfoMap ||
setFormulaObjectsCache(ctx, calcChains, data); _.isEmpty(ctx.formulaCache.formulaCellInfoMap)
) {
ctx.formulaCache.formulaCellInfoMap = {};
setFormulaCellInfoMap(ctx, calcChains, data);
} }
const { formulaObjects } = ctx.formulaCache; const { formulaCellInfoMap } = ctx.formulaCache;
// 4. Form a graph structure of references between formulas // 4. Form a graph structure of references between formulas
// basically fills parents in formulaObjects[i] // basically fills parents in formulaCellInfoMap[i]
const updateValueArray: any = []; const updateValueArray: any = [];
const arrayMatchCache: Record< const arrayMatchCache: Record<
string, string,
{ key: string; r: number; c: number; sheetId: string }[] { key: string; r: number; c: number; sheetId: string }[]
> = {}; > = {};
Object.keys(formulaObjects).forEach((key) => { Object.keys(formulaCellInfoMap).forEach((key) => {
const formulaObject = formulaObjects[key]; const formulaObject = formulaCellInfoMap[key];
arrayMatch( arrayMatch(
arrayMatchCache, arrayMatchCache,
formulaObject.formulaArray, formulaObject.formulaDependency,
formulaObjects, formulaCellInfoMap,
updateValueObjects, updateValueObjects,
(childKey: string) => { (childKey: string) => {
if (childKey in formulaObjects) { if (childKey in formulaCellInfoMap) {
const childFormulaObject = formulaObjects[childKey]; const childFormulaObject = formulaCellInfoMap[childKey];
// formulaObject.chidren[childKey] = 1; not needed // formulaObject.chidren[childKey] = 1; not needed
childFormulaObject.parents[key] = 1; childFormulaObject.parents[key] = 1;
} }
@ -1286,7 +1285,10 @@ export function execFunctionGroup(
}); });
// 5. Get list of affected formulas using the graph structure by depth-first traversal // 5. Get list of affected formulas using the graph structure by depth-first traversal
const formulaRunList = getFormulaRunList(updateValueArray, formulaObjects); const formulaRunList = getFormulaRunList(
updateValueArray,
formulaCellInfoMap
);
// 6. execute relevant formulas // 6. execute relevant formulas
executeAffectedFormulas(ctx, formulaRunList, calcChains); executeAffectedFormulas(ctx, formulaRunList, calcChains);
@ -1540,7 +1542,7 @@ export function createRangeHightlight(
return; return;
if ( if (
cellrange.sheetId === ctx.currentSheetId || cellrange.sheetId === ctx.currentSheetId ||
(cellrange.sheetId === -1 && (!cellrange.sheetId &&
ctx.formulaCache.rangetosheet === ctx.currentSheetId) ctx.formulaCache.rangetosheet === ctx.currentSheetId)
) { ) {
const rect = seletedHighlistByindex( const rect = seletedHighlistByindex(

View File

@ -3,6 +3,8 @@ import {
CellMatrix, CellMatrix,
Context, Context,
execfunction, execfunction,
FormulaCell,
FormulaCellInfo,
getcellFormula, getcellFormula,
getcellrange, getcellrange,
iscelldata, iscelldata,
@ -10,9 +12,9 @@ import {
} from ".."; } from "..";
// Make sure setFormulaObject() is executed *after* the cell modifications // Make sure setFormulaObject() is executed *after* the cell modifications
export function setFormulaObject( export function setFormulaCellInfo(
ctx: Context, ctx: Context,
formulaCell: any, formulaCell: FormulaCell,
data?: CellMatrix data?: CellMatrix
) { ) {
const key = `r${formulaCell.r}c${formulaCell.c}i${formulaCell.id}`; const key = `r${formulaCell.r}c${formulaCell.c}i${formulaCell.id}`;
@ -24,7 +26,7 @@ export function setFormulaObject(
data data
); );
if (_.isNil(calc_funcStr)) { if (_.isNil(calc_funcStr)) {
delete ctx.formulaCache.formulaObjects?.[key]; delete ctx.formulaCache.formulaCellInfoMap?.[key];
return; return;
} }
const txt1 = calc_funcStr.toUpperCase(); const txt1 = calc_funcStr.toUpperCase();
@ -33,8 +35,9 @@ export function setFormulaObject(
txt1.indexOf("OFFSET(") > -1 || txt1.indexOf("OFFSET(") > -1 ||
txt1.indexOf("INDEX(") > -1; txt1.indexOf("INDEX(") > -1;
const formulaArray = ctx.formulaCache.formulaArrayCache[calc_funcStr] || []; const formulaDependency =
if (formulaArray.length === 0) { ctx.formulaCache.formulaDependenciesMap[calc_funcStr] || [];
if (formulaDependency.length === 0) {
if (isOffsetFunc) { if (isOffsetFunc) {
isFunctionRange( isFunctionRange(
ctx, ctx,
@ -46,7 +49,7 @@ export function setFormulaObject(
(str_nb: string) => { (str_nb: string) => {
const range = getcellrange(ctx, _.trim(str_nb), formulaCell.id, data); const range = getcellrange(ctx, _.trim(str_nb), formulaCell.id, data);
if (!_.isNil(range)) { if (!_.isNil(range)) {
formulaArray.push(range); formulaDependency.push(range);
} }
} }
); );
@ -170,15 +173,15 @@ export function setFormulaObject(
continue; continue;
} }
formulaArray.push(range); formulaDependency.push(range);
} }
} }
} }
if (!ctx.formulaCache.formulaArrayCache[calc_funcStr]) if (!ctx.formulaCache.formulaDependenciesMap[calc_funcStr])
ctx.formulaCache.formulaArrayCache[calc_funcStr] = formulaArray; ctx.formulaCache.formulaDependenciesMap[calc_funcStr] = formulaDependency;
const item = { const item: FormulaCellInfo = {
formulaArray, formulaDependency,
calc_funcStr, calc_funcStr,
key, key,
r: formulaCell.r, r: formulaCell.r,
@ -189,8 +192,9 @@ export function setFormulaObject(
color: "w", color: "w",
}; };
if (!ctx.formulaCache.formulaObjects) ctx.formulaCache.formulaObjects = {}; if (!ctx.formulaCache.formulaCellInfoMap)
ctx.formulaCache.formulaObjects[key] = item; ctx.formulaCache.formulaCellInfoMap = {};
ctx.formulaCache.formulaCellInfoMap[key] = item;
} }
export function executeAffectedFormulas( export function executeAffectedFormulas(
@ -240,7 +244,7 @@ export function executeAffectedFormulas(
export function getFormulaRunList( export function getFormulaRunList(
updateValueArray: any[], updateValueArray: any[],
formulaObjects: any formulaCellInfoMap: any
) { ) {
const formulaRunList = []; const formulaRunList = [];
let stack = _.cloneDeep(updateValueArray); let stack = _.cloneDeep(updateValueArray);
@ -260,7 +264,7 @@ export function getFormulaRunList(
const cacheStack: any = []; const cacheStack: any = [];
Object.keys(formulaObject.parents).forEach((parentKey) => { Object.keys(formulaObject.parents).forEach((parentKey) => {
const parentFormulaObject = formulaObjects[parentKey]; const parentFormulaObject = formulaCellInfoMap[parentKey];
if (!_.isNil(parentFormulaObject)) { if (!_.isNil(parentFormulaObject)) {
cacheStack.push(parentFormulaObject); cacheStack.push(parentFormulaObject);
} }
@ -282,13 +286,13 @@ export function getFormulaRunList(
export const arrayMatch = ( export const arrayMatch = (
arrayMatchCache: any, arrayMatchCache: any,
formulaArray: any, formulaDependency: any,
_formulaObjects: any, _formulaCellInfoMap: any,
_updateValueObjects: any, _updateValueObjects: any,
func: any func: any
) => { ) => {
for (let a = 0; a < formulaArray.length; a += 1) { for (let a = 0; a < formulaDependency.length; a += 1) {
const range = formulaArray[a]; const range = formulaDependency[a];
const cacheKey = `r${range.row[0]}${range.row[1]}c${range.column[0]}${range.column[1]}id${range.sheetId}`; const cacheKey = `r${range.row[0]}${range.row[1]}c${range.column[0]}${range.column[1]}id${range.sheetId}`;
if (cacheKey in arrayMatchCache) { if (cacheKey in arrayMatchCache) {
const amc: any[] = arrayMatchCache[cacheKey]; const amc: any[] = arrayMatchCache[cacheKey];
@ -302,7 +306,7 @@ export const arrayMatch = (
const key = `r${r}c${c}i${range.sheetId}`; const key = `r${r}c${c}i${range.sheetId}`;
func(key, r, c, range.sheetId); func(key, r, c, range.sheetId);
if ( if (
(_formulaObjects && key in _formulaObjects) || (_formulaCellInfoMap && key in _formulaCellInfoMap) ||
(_updateValueObjects && key in _updateValueObjects) (_updateValueObjects && key in _updateValueObjects)
) { ) {
functionArr.push({ functionArr.push({
@ -315,7 +319,7 @@ export const arrayMatch = (
} }
} }
if (_formulaObjects || _updateValueObjects) { if (_formulaCellInfoMap || _updateValueObjects) {
arrayMatchCache[cacheKey] = functionArr; arrayMatchCache[cacheKey] = functionArr;
} }
} }

View File

@ -175,7 +175,7 @@ export function goToLink(
scrollbarX.scrollLeft = col_pre; scrollbarX.scrollLeft = col_pre;
scrollbarY.scrollLeft = row_pre; scrollbarY.scrollLeft = row_pre;
ctx.luckysheet_select_save = normalizeSelection(ctx, [range]); ctx.luckysheet_select_save = normalizeSelection(ctx, [range]);
changeSheet(ctx, range.sheetId); changeSheet(ctx, range.sheetId || ctx.currentSheetId);
} }
ctx.linkCard = undefined; ctx.linkCard = undefined;
} }

View File

@ -1,7 +1,7 @@
import { Context, getFlowdata } from "../context"; import { Context, getFlowdata } from "../context";
import { CellMatrix, Selection } from "../types"; import { CellMatrix, Selection } from "../types";
import { execFunctionGroup } from "./formula"; import { execFunctionGroup } from "./formula";
import { setFormulaObject } from "./formulaHelper"; import { setFormulaCellInfo } from "./formulaHelper";
function runExecFunction( function runExecFunction(
ctx: Context, ctx: Context,
@ -13,7 +13,7 @@ function runExecFunction(
for (let s = 0; s < range.length; s += 1) { for (let s = 0; s < range.length; s += 1) {
for (let r = range[s].row[0]; r <= range[s].row[1]; r += 1) { for (let r = range[s].row[0]; r <= range[s].row[1]; r += 1) {
for (let c = range[s].column[0]; c <= range[s].column[1]; c += 1) { for (let c = range[s].column[0]; c <= range[s].column[1]; c += 1) {
setFormulaObject(ctx, { r, c, id: index }, data); setFormulaCellInfo(ctx, { r, c, id: index }, data);
ctx.formulaCache.execFunctionExist.push({ r, c, i: index }); ctx.formulaCache.execFunctionExist.push({ r, c, i: index });
} }
} }

View File

@ -1152,6 +1152,7 @@ export function insertRowCol(
} }
refreshLocalMergeData(merge_new, file); refreshLocalMergeData(merge_new, file);
ctx.formulaCache.formulaCellInfoMap = null;
// if (type === "row") { // if (type === "row") {
// const scrollLeft = $("#luckysheet-cell-main").scrollLeft(); // const scrollLeft = $("#luckysheet-cell-main").scrollLeft();
@ -2062,6 +2063,7 @@ export function deleteRowCol(
file.hyperlink = newHyperlink; file.hyperlink = newHyperlink;
refreshLocalMergeData(merge_new, file); refreshLocalMergeData(merge_new, file);
ctx.formulaCache.formulaCellInfoMap = null;
if (file.id === ctx.currentSheetId) { if (file.id === ctx.currentSheetId) {
ctx.config = cfg; ctx.config = cfg;

View File

@ -6,6 +6,7 @@ import { locale } from "../locale";
import { Settings } from "../settings"; import { Settings } from "../settings";
import { CellMatrix, Sheet } from "../types"; import { CellMatrix, Sheet } from "../types";
import { generateRandomSheetName, getSheetIndex } from "../utils"; import { generateRandomSheetName, getSheetIndex } from "../utils";
import { setFormulaCellInfo } from "./formulaHelper";
function storeSheetParam(ctx: Context) { function storeSheetParam(ctx: Context) {
const index = getSheetIndex(ctx, ctx.currentSheetId); const index = getSheetIndex(ctx, ctx.currentSheetId);
@ -194,7 +195,7 @@ export function updateSheet(ctx: Context, newData: Sheet[]) {
const { data, row, column } = newDatum; const { data, row, column } = newDatum;
const index = getSheetIndex(ctx, newDatum.id!) as number; const index = getSheetIndex(ctx, newDatum.id!) as number;
if (data != null) { if (data != null) {
// 如果row和column存在的话则进行row和column和data进行比较如果row和column不存在的话则进行data和default进行比较。 // If row and column exist, compare row and column with data. If row and column do not exist, compare data with default.
let lastRowNum = data.length; let lastRowNum = data.length;
let lastColNum = data[0].length; let lastColNum = data[0].length;
if (row != null && column != null && row > 0 && column > 0) { if (row != null && column != null && row > 0 && column > 0) {
@ -210,6 +211,7 @@ export function updateSheet(ctx: Context, newData: Sheet[]) {
for (let i = 0; i < data.length; i += 1) { for (let i = 0; i < data.length; i += 1) {
for (let j = 0; j < data[i].length; j += 1) { for (let j = 0; j < data[i].length; j += 1) {
expandedData[i][j] = data[i][j]; expandedData[i][j] = data[i][j];
setFormulaCellInfo(ctx, { r: i, c: j, id: newDatum.id! }, data);
} }
} }
newDatum.data = expandedData; newDatum.data = expandedData;
@ -220,6 +222,14 @@ export function updateSheet(ctx: Context, newData: Sheet[]) {
} }
} else if (newDatum.celldata != null) { } else if (newDatum.celldata != null) {
initSheetData(ctx, index, newDatum); initSheetData(ctx, index, newDatum);
const _index = getSheetIndex(ctx, newDatum.id!) as number;
newDatum.celldata?.forEach((d) => {
setFormulaCellInfo(
ctx,
{ r: d.r, c: d.c, id: newDatum.id! },
ctx.luckysheetfile[_index].data
);
});
} }
}); });
} }

View File

@ -41,7 +41,7 @@ import {
import { showLinkCard } from "./hyperlink"; import { showLinkCard } from "./hyperlink";
import { cfSplitRange } from "./conditionalFormat"; import { cfSplitRange } from "./conditionalFormat";
import { getCellTextInfo } from "./text"; import { getCellTextInfo } from "./text";
import { setFormulaObject } from "./formulaHelper"; import { setFormulaCellInfo } from "./formulaHelper";
type ToolbarItemClickHandler = ( type ToolbarItemClickHandler = (
ctx: Context, ctx: Context,
@ -903,7 +903,7 @@ export function autoSelectionFormula(
if (!isfalse) { if (!isfalse) {
ctx.formulaCache.execFunctionExist.reverse(); ctx.formulaCache.execFunctionExist.reverse();
ctx.formulaCache.execFunctionExist.forEach((formulaCell) => { ctx.formulaCache.execFunctionExist.forEach((formulaCell) => {
setFormulaObject( setFormulaCellInfo(
ctx, ctx,
{ r: formulaCell.r, c: formulaCell.c, id: ctx.currentSheetId }, { r: formulaCell.r, c: formulaCell.c, id: ctx.currentSheetId },
flowdata flowdata

View File

@ -314,3 +314,45 @@ export type GlobalCache = {
export type SingleRange = { row: number[]; column: number[] }; export type SingleRange = { row: number[]; column: number[] };
export type Range = SingleRange[]; export type Range = SingleRange[];
// FORMULA
export type FormulaDependency = {
row: [number, number];
column: [number, number];
sheetId: string | undefined;
};
export type FormulaDependenciesMap = {
[formula: string]: FormulaDependency[];
};
type AncestorFormulaCell = {
[rxcxix: string]: number;
};
export type FormulaCellInfo = {
formulaDependency: FormulaDependency[];
calc_funcStr: string;
key: string;
r: number;
c: number;
id: string;
parents: AncestorFormulaCell;
chidren: AncestorFormulaCell;
color: string;
};
export type FormulaCellInfoMap = {
[rxcxix: string]: FormulaCellInfo;
};
export type FormulaCell = {
r: number;
c: number;
id: string;
parent?: AncestorFormulaCell;
func?: [boolean, number, string];
color?: string;
chidren?: AncestorFormulaCell;
times?: number;
};

View File

@ -1,3 +1,4 @@
import _ from "lodash";
import { import {
getDropdownList, getDropdownList,
getFlowdata, getFlowdata,
@ -83,12 +84,19 @@ const DataVerification: React.FC = () => {
getSheetIndex(ctx, ctx.currentSheetId) as number getSheetIndex(ctx, ctx.currentSheetId) as number
].dataVerification ?? {}; ].dataVerification ?? {};
const str = range[range.length - 1].row[0]; const str = range?.[range.length - 1]?.row[0];
const edr = range[range.length - 1].row[1]; const edr = range?.[range.length - 1]?.row[1];
const stc = range[range.length - 1].column[0]; const stc = range?.[range.length - 1]?.column[0];
const edc = range[range.length - 1].column[1]; const edc = range?.[range.length - 1]?.column[1];
const d = getFlowdata(ctx); const d = getFlowdata(ctx);
if (!d) return; if (
!d ||
_.isNil(str) ||
_.isNil(stc) ||
_.isNil(edr) ||
_.isNil(edc)
)
return;
for (let r = str; r <= edr; r += 1) { for (let r = str; r <= edr; r += 1) {
for (let c = stc; c <= edc; c += 1) { for (let c = stc; c <= edc; c += 1) {
const key = `${r}_${c}`; const key = `${r}_${c}`;
@ -116,10 +124,12 @@ const DataVerification: React.FC = () => {
const currentDataVerification = const currentDataVerification =
ctx.luckysheetfile[getSheetIndex(ctx, ctx.currentSheetId) as number] ctx.luckysheetfile[getSheetIndex(ctx, ctx.currentSheetId) as number]
.dataVerification ?? {}; .dataVerification ?? {};
const str = range[range.length - 1].row[0]; const str = range?.[range.length - 1]?.row[0];
const edr = range[range.length - 1].row[1]; const edr = range?.[range.length - 1]?.row[1];
const stc = range[range.length - 1].column[0]; const stc = range?.[range.length - 1]?.column[0];
const edc = range[range.length - 1].column[1]; const edc = range?.[range.length - 1]?.column[1];
if (_.isNil(str) || _.isNil(stc) || _.isNil(edr) || _.isNil(edc))
return;
for (let r = str; r <= edr; r += 1) { for (let r = str; r <= edr; r += 1) {
for (let c = stc; c <= edc; c += 1) { for (let c = stc; c <= edc; c += 1) {
delete currentDataVerification[`${r}_${c}`]; delete currentDataVerification[`${r}_${c}`];

View File

@ -20,6 +20,7 @@ import {
locale, locale,
calcSelectionInfo, calcSelectionInfo,
groupValuesRefresh, groupValuesRefresh,
setFormulaCellInfoMap,
} from "@fortune-sheet/core"; } from "@fortune-sheet/core";
import React, { import React, {
useMemo, useMemo,
@ -440,7 +441,12 @@ const Workbook = React.forwardRef<WorkbookInstance, Settings & AdditionalProps>(
newData.forEach((newDatum) => { newData.forEach((newDatum) => {
const index = getSheetIndex(draftCtx, newDatum.id!) as number; const index = getSheetIndex(draftCtx, newDatum.id!) as number;
const sheet = draftCtx.luckysheetfile?.[index]; const sheet = draftCtx.luckysheetfile?.[index];
initSheetData(draftCtx, sheet, index); const cellMatrixData = initSheetData(draftCtx, sheet, index);
setFormulaCellInfoMap(
draftCtx,
sheet.calcChain,
cellMatrixData || undefined
);
}); });
} }
if (mergedSettings.devicePixelRatio > 0) { if (mergedSettings.devicePixelRatio > 0) {