import { ID, ICell, IStoreState, ICellNN } from '../../index.data'; import { ParsedResult } from '../../formula-parser/data'; import * as R from 'rambda'; import Parser from '../../formula-parser/parser'; import { getCellIdByStr, sumLeft, sumRight, sumChildren, sumSiblings, sumSamePosition, getRangeValue, } from '.'; import { ERROR_REF } from '../../formula-parser/error'; import { getCell } from '../selectors'; import { getTypedId } from '../../utils'; import equals from 'fast-deep-equal'; import { FormulaManagerConfig } from '../../formula-manager/index.data'; export interface CellWithDepsInState { cells: Record; cellDeps: Record; varDeps: Record; } // 计算完成后, 在很多情况下, 例如 新增行时, 由于value根本未变, 所以可以还原state.cell // 集中式的还原, 可以避免在每个组件中再单独判断 export function restoreCellRefIfNeeded( state: IStoreState, cellWithDeps: CellWithDepsInState, ): CellWithDepsInState { const oldCells = state.cells; const newCells = R.map(cell => { const oldCell = oldCells[cell!.id]; return equals(oldCell, cell) ? oldCell : cell; }, cellWithDeps.cells); return Object.assign({}, cellWithDeps, { cells: newCells }); } // 如果cells里的cell与state里的cell不同, 则返回 // 作用与restoreCellIfNeeded相似, 避免无效刷新 export function extractChangedCells(state: IStoreState, cells: ICell[]): ICellNN[] { return R.filter(cell => { const oldCell = state.cells[cell!.id]; return !equals(oldCell, cell); }, cells.filter(Boolean)) as any; } /** * 将parser计算出的结果, 转换为state的patch * partialState_dangeously 会被内部改动 (性能原因) */ export function mergeParsedResults( partialState_dangeously: CellWithDepsInState, parsedResults: Record, ): CellWithDepsInState { // for 性能 let result = partialState_dangeously; const parsedResultsKeys = Object.keys(parsedResults); parsedResultsKeys.forEach(_cellId => { let acc = result; const cellId = getTypedId(_cellId); const cell = partialState_dangeously.cells[cellId]; const parsedResult = parsedResults[cellId]; // for 性能 // 这个assign操作, 在280行时耗时1.5秒, 所以取消了 // const _cells = Object.assign({}, acc.cells); // 目前这里的性能没有瓶颈, 但因为修改了acc=>partialState入口参数, 所以非常危险!!! const _cells = acc.cells; const newCell = Object.assign({}, cell, { value: parsedResult.result, error: parsedResult.error, }); _cells[cellId] = newCell; // _cells[cellId] = { // ...cell!, // value: parsedResult.result, // error: parsedResult.error, // }; result = { cells: _cells, cellDeps: parsedResult.deps && parsedResult.deps.cells ? setCellDeps(acc.cellDeps, cellId, parsedResult.deps.cells!) : acc.cellDeps, varDeps: parsedResult.deps && parsedResult.deps.variables ? setVarDeps(acc.varDeps, cellId, parsedResult.deps.variables!) : acc.cellDeps, }; }); return result; } /** * 将 主体->[依赖1, 依赖2] => [依赖1: [主体], 依赖2: [主体]], 以便适配有向无环图 * 单元格3有依赖[4,5], 4,5的变动将触发3的变动 * 那么从parser中计算出的结果是: [4,5] * 需要转换为需要的格式(有向无环图, 4:[3] 表示4变动将触发3变动): * {4:[3], 5: [3]} */ export function setCellDeps( cellDeps: Record, hostCellId: any, deps: ID[], ): Record { // 第1步: 从cellDeps中, 排除hostCellId // 下述代码, 屏蔽部分均为性能对比用 // const cellDeps_exceptHostId = R.map( // cellDep => cellDep.filter(dep => hostCellId !== dep), // cellDeps || {}, // ); // 不能用_.filter, 不知为何, 会卡死 // const cellDeps_exceptHostId_0 = R.map( // cellDep => _.filter(cellDep, dep => hostCellId !== dep), // cellDeps || {}, // ); /* const cellDeps_exceptHostId_1: Record = {}; Object.keys(cellDeps || ({} as Record)).forEach(id => { cellDeps_exceptHostId_1[id] = cellDeps[id].filter(dep => hostCellId !== dep); }); const cellDeps_exceptHostId_2: Record = {}; Object.keys(cellDeps || ({} as Record)).forEach(id => { cellDeps_exceptHostId_2[id] = []; cellDeps[id].forEach(dep => { if (hostCellId !== dep) cellDeps_exceptHostId_2[id].push(dep); }); }); const cellDeps_exceptHostId_3: Record = {}; for (let i = 0, keys = Object.keys(cellDeps || ({} as Record)); i < keys.length; i++) { const id = keys[i]; cellDeps_exceptHostId_3[id] = []; for (let j = 0; j < cellDeps[id].length; j++) { const dep = cellDeps[id][j]; if (hostCellId !== dep) cellDeps_exceptHostId_3[id].push(dep); } } */ // 第2步: 将hostCellId, 分别添加到 deps 的每个元素, 生成初步的有向无环图 输入 /* const newCellDeps = deps.reduce( // (acc, depCellId) => ({ ...acc, [depCellId]: [...(acc[depCellId] || []), hostCellId] }), (acc, depCellId) => Object.assign({}, acc, { [depCellId]: [...(acc[depCellId] || []), hostCellId] }), cellDeps_exceptHostId, ); return newCellDeps; */ // 超快, 比别的方法快50倍以上 const cellDeps_exceptHostId = R.map( cellDep => R.filter(dep => hostCellId !== dep, cellDep), cellDeps || {}, ); // 危险 , 但为了性能, 280行节省0.7s // const result: Record = Object.assign({}, cellDeps_exceptHostId); const result = cellDeps_exceptHostId; for (let i = 0; i < deps.length; i++) { let depCellId = deps[i]; result[depCellId] = (result[depCellId] || []).concat(hostCellId); } return result; } export function setVarDeps( varDeps: Record, hostCellId: any, deps: ID[], ): Record { // 第1步: 从varDeps中, 排除hostCellId const varDeps_exceptHostId = R.map( varDep => varDep.filter(dep => hostCellId !== dep), varDeps || {}, ); // 第2步: 将hostCellId, 分别添加到 deps 的每个元素, 生成初步的有向无环图 输入 const newVarDeps = deps.reduce( // (acc, depCellId) => ({ ...acc, [depCellId]: [...(acc[depCellId] || [] as any), hostCellId] }), (acc, depCellId) => Object.assign({}, acc, { [depCellId]: [...(acc[depCellId] || ([] as any)), hostCellId] }), varDeps_exceptHostId, ); return newVarDeps; } /** * 获取默认的公式parser, ( 不会递归计算 ) */ export function getParser(state: IStoreState, id: ID, options?: FormulaManagerConfig) { const getCellValue = (_cellId: ID) => ({ result: getCell(state, _cellId, options)!.value, deps: { cells: [_cellId as any] }, error: null, }); return new Parser({ getCellValue: sCell => { const depCellId = getCellIdByStr(state, sCell, options); if (!depCellId) throw new Error(ERROR_REF); return getCellValue(depCellId)!; }, getRangeValue: ([sStartCellId, sEndCellId]: [string, string]) => { const startCellId = getCellIdByStr(state, sStartCellId, options); const endCellId = getCellIdByStr(state, sEndCellId, options); return getRangeValue(state, startCellId, endCellId); }, // 下面四个公式, 在这里都不需要递归调用, 所以无需 getCellValue sumLeft: (args: any[] | undefined) => args ? sumLeft(state, id, args[0], args[1] /*, getCellValue*/) : sumLeft(state, id, undefined, undefined), sumRight: (args: any[] | undefined) => args ? sumRight(state, id, args[0], args[1] /*, getCellValue*/) : sumRight(state, id, undefined, undefined), sumSamePosition: (args: any[] | undefined) => sumSamePosition(state, id, args ? args[0] : undefined), sumChildren: () => sumChildren(state, id /* getCellValue*/), sumSiblings: () => sumSiblings(state, id /* getCellValue*/), variables: state.variables, }); } /** * 若存在 varDeps: [A1], cellDeps: {B: [A1, A2]} * 则 remove A1 后, 会变成 varDeps:[], cellDeps: {B: [A2]} */ export function removeCellFromDeps( cellId: ID, { varDeps, cellDeps }: { varDeps: Record; cellDeps: Record }, ) { return { varDeps: Object.keys(varDeps).reduce( (acc, varName) => ({ ...acc, [varName]: varDeps[varName].filter(it => it !== cellId) }), {}, ), cellDeps: Object.keys(cellDeps).reduce( (acc, cellName) => ({ ...acc, [cellName]: cellDeps[cellName].filter(it => it !== cellId) }), {}, ), }; }