@univerjs/engine-formula 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -9740,6 +9740,89 @@ function baseValueObjectToArrayValueObject(valueObject) {
9740
9740
  if (valueObject.isArray()) return valueObject;
9741
9741
  return ArrayValueObject.createByArray([[valueObject.getValue()]]);
9742
9742
  }
9743
+ function getEqualityValueKeys(value) {
9744
+ const keys = [];
9745
+ if (value.isString()) keys.push(normalizeTextForComparison(String(value.getValue())));
9746
+ else if (!value.isNumber()) return keys;
9747
+ const number = value.isNumber() ? value : createCriteriaValueObject(String(value.getValue()), value.getDateSystem());
9748
+ if (number.isNumber() && Number.isFinite(Number(number.getValue()))) keys.push(stripErrorMargin(Number(number.getValue())));
9749
+ return keys;
9750
+ }
9751
+ function buildEqualityIndex(range) {
9752
+ const index = /* @__PURE__ */ new Map();
9753
+ range.iterator((value, row, column) => {
9754
+ if (!value) return;
9755
+ const position = [row, column];
9756
+ for (const key of getEqualityValueKeys(value)) {
9757
+ let matches = index.get(key);
9758
+ if (!matches) {
9759
+ matches = [];
9760
+ index.set(key, matches);
9761
+ }
9762
+ matches.push(position);
9763
+ }
9764
+ });
9765
+ return index;
9766
+ }
9767
+ function getIndexedPositions(index, keys, range) {
9768
+ const buckets = keys.map((key) => index.get(key) ?? []);
9769
+ if (buckets.reduce((count, bucket) => count + bucket.length, 0) >= range.getRowCount() * range.getColumnCount() / 2) return;
9770
+ if (buckets.length === 1) return buckets[0];
9771
+ return Array.from(new Set(buckets.flat())).sort((a, b) => a[0] - b[0] || a[1] - b[1]);
9772
+ }
9773
+ function pickRangePositions(range, positions) {
9774
+ return ArrayValueObject.create({
9775
+ calculateValueList: positions.map(([row, column]) => [range.get(row, column)]),
9776
+ rowCount: positions.length,
9777
+ columnCount: 1,
9778
+ unitId: "",
9779
+ sheetId: "",
9780
+ row: -1,
9781
+ column: -1
9782
+ });
9783
+ }
9784
+ function getExactCriteriaKeys(criteria) {
9785
+ if (!criteria || !criteria.isString() && !criteria.isNumber()) return;
9786
+ const [operator, value] = criteria.isString() ? findCompareToken(String(criteria.getValue()), criteria.getDateSystem()) : ["=", criteria];
9787
+ if (operator !== "=" || value.isDateFormat()) return;
9788
+ if (value.isString() && (value.getValue() === "" || isWildcard(String(value.getValue())))) return;
9789
+ const keys = getEqualityValueKeys(value);
9790
+ return keys.length > 0 ? keys : void 0;
9791
+ }
9792
+ function getCriteriaCacheKey(criteria) {
9793
+ if (criteria.length === 0 || criteria.some((value) => getExactCriteriaKeys(value) === void 0)) return;
9794
+ const values = criteria.map((value) => value.getValue());
9795
+ return values.length === 1 ? values[0] : JSON.stringify(values);
9796
+ }
9797
+ function getCriteriaPositions(pairs, indexes, row, column) {
9798
+ for (const { range, criteriaArray } of pairs) {
9799
+ const keys = getExactCriteriaKeys(criteriaArray.get(row, column));
9800
+ if (!keys) continue;
9801
+ let index = indexes.get(range);
9802
+ if (!index) {
9803
+ index = buildEqualityIndex(range);
9804
+ indexes.set(range, index);
9805
+ }
9806
+ const positions = getIndexedPositions(index, keys, range);
9807
+ if (positions !== void 0) return positions;
9808
+ }
9809
+ }
9810
+ function getConditionalAggregateResult(formulaName, comparison, targetRange) {
9811
+ if (formulaName === "COUNTIFS") {
9812
+ let count = 0;
9813
+ comparison.iterator((value) => {
9814
+ if ((value === null || value === void 0 ? void 0 : value.isBoolean()) && value.getValue() === true) count++;
9815
+ });
9816
+ return NumberValueObject.create(count);
9817
+ }
9818
+ const picked = targetRange === null || targetRange === void 0 ? void 0 : targetRange.pick(comparison);
9819
+ switch (formulaName) {
9820
+ case "SUMIFS": return picked.sum();
9821
+ case "AVERAGEIFS": return picked.sum().divided(picked.count());
9822
+ case "MAXIFS": return picked.getColumnCount() === 0 ? NumberValueObject.create(0) : picked.max();
9823
+ case "MINIFS": return picked.getColumnCount() === 0 ? NumberValueObject.create(0) : picked.min();
9824
+ }
9825
+ }
9743
9826
  /**
9744
9827
  * Get the paired range and criteria result for COUNTIFS, SUMIFS, etc.
9745
9828
  */
@@ -9758,50 +9841,48 @@ function getPairedRangeAndCriteriaResult(variants, params) {
9758
9841
  }
9759
9842
  if (rangeAndCriteriaArrays.length === 0) return [];
9760
9843
  const results = [];
9761
- /**
9762
- * Iterate through all criteria values for each dimension, calculate the comparison result with the corresponding range, and then calculate the Boolean intersection of all comparison results as the final result for that dimension criteria value.
9763
- * Then calculate this dimension criteria value's result.
9764
- * This avoiding store the all dimension comparison result in memory and then calculating all dimension criteria value's result, which may cause memory overflow when the range is large and there are multiple dimension criteria.
9765
- * For example, `=COUNTIFS(Q$3:Q$10002,C$3:C$5002,R$3:R$10002,L6)`.
9766
- */
9844
+ const arrayPairs = rangeAndCriteriaArrays.filter((_, index) => {
9845
+ const criteria = variants[index * 2 + 1];
9846
+ return criteria.isArray() && criteria.getRowCount() * criteria.getColumnCount() > 1;
9847
+ });
9848
+ const useEqualityIndex = isNumberSensitive && maxRowLength * maxColumnLength > 1 && arrayPairs.length > 0;
9849
+ const repeatedResults = /* @__PURE__ */ new Map();
9850
+ const equalityIndexes = /* @__PURE__ */ new Map();
9767
9851
  rangeAndCriteriaArrays[0].criteriaArray.iterator((_, rowIndex, columnIndex) => {
9768
9852
  let finalCompareResult;
9769
- for (let i = 0; i < rangeAndCriteriaArrays.length; i++) {
9770
- const { range, criteriaArray } = rangeAndCriteriaArrays[i];
9853
+ let positions;
9854
+ const cacheKey = useEqualityIndex ? getCriteriaCacheKey(arrayPairs.map(({ criteriaArray }) => criteriaArray.get(rowIndex, columnIndex))) : void 0;
9855
+ const cached = cacheKey === void 0 ? void 0 : repeatedResults.get(cacheKey);
9856
+ if (cached) {
9857
+ results[rowIndex] ??= [];
9858
+ results[rowIndex][columnIndex] = cached;
9859
+ return;
9860
+ }
9861
+ if (useEqualityIndex) {
9862
+ positions = getCriteriaPositions(arrayPairs, equalityIndexes, rowIndex, columnIndex);
9863
+ if ((positions === null || positions === void 0 ? void 0 : positions.length) === 0) {
9864
+ results[rowIndex] ??= [];
9865
+ results[rowIndex][columnIndex] = formulaName === "AVERAGEIFS" ? ErrorValueObject.create("#DIV/0!") : NumberValueObject.create(0);
9866
+ return;
9867
+ }
9868
+ }
9869
+ rangeAndCriteriaArrays.forEach(({ range: sourceRange, criteriaArray }) => {
9870
+ const range = positions === void 0 ? sourceRange : pickRangePositions(sourceRange, positions);
9771
9871
  const criteriaValueObject = criteriaArray.get(rowIndex, columnIndex);
9772
- if (!criteriaValueObject) continue;
9872
+ if (!criteriaValueObject) return;
9773
9873
  let compareResult = valueObjectCompare(range, criteriaValueObject);
9774
9874
  if (isNumberSensitive) compareResult = filterSameValueObjectResult(compareResult, range, criteriaValueObject);
9775
9875
  if (finalCompareResult === void 0) {
9776
9876
  finalCompareResult = compareResult;
9777
- continue;
9877
+ return;
9778
9878
  }
9779
9879
  finalCompareResult = booleanObjectIntersection(finalCompareResult, compareResult);
9780
- }
9781
- let result;
9782
- if (formulaName === "COUNTIFS") {
9783
- let count = 0;
9784
- finalCompareResult.iterator((value) => {
9785
- if ((value === null || value === void 0 ? void 0 : value.isBoolean()) && value.getValue() === true) count++;
9786
- });
9787
- result = NumberValueObject.create(count);
9788
- } else if (formulaName === "SUMIFS") result = targetRange.pick(finalCompareResult).sum();
9789
- else if (formulaName === "AVERAGEIFS") {
9790
- const picked = targetRange.pick(finalCompareResult);
9791
- const sum = picked.sum();
9792
- const count = picked.count();
9793
- result = sum.divided(count);
9794
- } else if (formulaName === "MAXIFS") {
9795
- const picked = targetRange.pick(finalCompareResult);
9796
- if (picked.getColumnCount() === 0) result = NumberValueObject.create(0);
9797
- else result = picked.max();
9798
- } else if (formulaName === "MINIFS") {
9799
- const picked = targetRange.pick(finalCompareResult);
9800
- if (picked.getColumnCount() === 0) result = NumberValueObject.create(0);
9801
- else result = picked.min();
9802
- }
9880
+ });
9881
+ const aggregateRange = positions === void 0 || !targetRange ? targetRange : pickRangePositions(targetRange, positions);
9882
+ const result = getConditionalAggregateResult(formulaName, finalCompareResult, aggregateRange);
9803
9883
  if (!results[rowIndex]) results[rowIndex] = [];
9804
9884
  results[rowIndex][columnIndex] = result;
9885
+ if (cacheKey !== void 0 && result) repeatedResults.set(cacheKey, result);
9805
9886
  });
9806
9887
  return results;
9807
9888
  }
@@ -36215,6 +36296,19 @@ var Sumif = class extends BaseFunction {
36215
36296
  let _criteria = criteria;
36216
36297
  if (criteria.isReferenceObject()) _criteria = criteria.toArrayValueObject();
36217
36298
  if (_criteria.isArray()) {
36299
+ const criteriaArray = _criteria;
36300
+ const rangeArray = range.toArrayValueObject();
36301
+ const targetArray = sumRange ? sumRange.toArrayValueObject() : rangeArray;
36302
+ if (criteriaArray.getRowCount() * criteriaArray.getColumnCount() > 1 && rangeArray.getRowCount() === targetArray.getRowCount() && rangeArray.getColumnCount() === targetArray.getColumnCount()) {
36303
+ const values = getPairedRangeAndCriteriaResult([rangeArray, criteriaArray], {
36304
+ formulaName: "SUMIFS",
36305
+ maxRowLength: criteriaArray.getRowCount(),
36306
+ maxColumnLength: criteriaArray.getColumnCount(),
36307
+ isNumberSensitive: true,
36308
+ targetRange: targetArray
36309
+ });
36310
+ return criteriaArray.mapValue((_, row, column) => values[row][column]);
36311
+ }
36218
36312
  const resultArray = _criteria.mapValue((criteriaObject) => this._handleSingleObject(range, criteriaObject, sumRange));
36219
36313
  if (resultArray.getRowCount() === 1 && resultArray.getColumnCount() === 1) return resultArray.get(0, 0);
36220
36314
  return resultArray;
@@ -37200,6 +37294,19 @@ var Averageif = class extends BaseFunction {
37200
37294
  let _criteria = criteria;
37201
37295
  if (criteria.isReferenceObject()) _criteria = criteria.toArrayValueObject();
37202
37296
  if (_criteria.isArray()) {
37297
+ const criteriaArray = _criteria;
37298
+ const rangeArray = range.toArrayValueObject();
37299
+ const targetArray = averageRange ? averageRange.toArrayValueObject() : rangeArray;
37300
+ if (criteriaArray.getRowCount() * criteriaArray.getColumnCount() > 1 && rangeArray.getRowCount() === targetArray.getRowCount() && rangeArray.getColumnCount() === targetArray.getColumnCount()) {
37301
+ const values = getPairedRangeAndCriteriaResult([rangeArray, criteriaArray], {
37302
+ formulaName: "AVERAGEIFS",
37303
+ maxRowLength: criteriaArray.getRowCount(),
37304
+ maxColumnLength: criteriaArray.getColumnCount(),
37305
+ isNumberSensitive: true,
37306
+ targetRange: targetArray
37307
+ });
37308
+ return criteriaArray.mapValue((_, row, column) => values[row][column]);
37309
+ }
37203
37310
  const resultArray = _criteria.mapValue((criteriaObject) => this._handleSingleObject(range, criteriaObject, averageRange));
37204
37311
  if (resultArray.getRowCount() === 1 && resultArray.getColumnCount() === 1) return resultArray.get(0, 0);
37205
37312
  return resultArray;
@@ -37618,6 +37725,18 @@ var Countif = class extends BaseFunction {
37618
37725
  }
37619
37726
  }
37620
37727
  if (_criteria.isArray()) {
37728
+ const criteriaArray = _criteria;
37729
+ const rangeArray = range.toArrayValueObject();
37730
+ if (criteriaArray.getRowCount() * criteriaArray.getColumnCount() > 1) {
37731
+ const normalized = criteria.isReferenceObject() ? criteriaArray.mapValue((value) => value.isNull() ? NumberValueObject.create(0) : value) : criteriaArray;
37732
+ const values = getPairedRangeAndCriteriaResult([rangeArray, normalized], {
37733
+ formulaName: "COUNTIFS",
37734
+ maxRowLength: criteriaArray.getRowCount(),
37735
+ maxColumnLength: criteriaArray.getColumnCount(),
37736
+ isNumberSensitive: true
37737
+ });
37738
+ return criteriaArray.mapValue((_, row, column) => values[row][column]);
37739
+ }
37621
37740
  const resultArray = _criteria.mapValue((criteriaObject) => this._handleSingleObject(range, criteria.isReferenceObject() && criteriaObject.isNull() ? NumberValueObject.create(0) : criteriaObject));
37622
37741
  if (resultArray.getRowCount() === 1 && resultArray.getColumnCount() === 1) return resultArray.get(0, 0);
37623
37742
  return resultArray;
@@ -43600,7 +43719,7 @@ function getObjectValue(result, isUseStrip = false) {
43600
43719
  //#endregion
43601
43720
  //#region package.json
43602
43721
  var name = "@univerjs/engine-formula";
43603
- var version = "1.0.1";
43722
+ var version = "1.0.3";
43604
43723
 
43605
43724
  //#endregion
43606
43725
  //#region src/services/global-computing-status.service.ts