Skip to content

Commit

Permalink
fix(formula): sum get error when cell has error (#1306)
Browse files Browse the repository at this point in the history
* fix(formula): get rich text data stream as content

* fix(formula): get datastream number when insert function

* fix(formula): sum get error

* fix(formula): plus gets error when cell has error

* fix(formula): sum,var,std
  • Loading branch information
Dushusir committed Feb 7, 2024
1 parent 51ae0e1 commit a13c450
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 37 deletions.
Expand Up @@ -87,6 +87,54 @@ describe('arrayValueObject test', () => {
});
});

describe('Count', () => {
it('Normal count', () => {
const originValueObject = new ArrayValueObject({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false],
[0, '100', '2.34', 'test', -3],
]),
rowCount: 2,
columnCount: 5,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
expect(originValueObject.count()?.getValue()).toBe(6);
});
it('CountA', () => {
const originValueObject = new ArrayValueObject({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false],
[0, '100', '2.34', 'test', -3],
]),
rowCount: 2,
columnCount: 5,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
expect(originValueObject.countA()?.getValue()).toBe(10);
});
it('CountBlank', () => {
const originValueObject = new ArrayValueObject({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false],
[0, '100', '2.34', 'test', -3],
]),
rowCount: 2,
columnCount: 5,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});
expect(originValueObject.countBlank()?.getValue()).toBe(0);
});
});

describe('pick', () => {
it('normal', () => {
const pickArrayValueObject = new ArrayValueObject({
Expand Down Expand Up @@ -143,6 +191,34 @@ describe('arrayValueObject test', () => {
});
});

describe('sum', () => {
it('normal', () => {
expect(originArrayValueObject.sum().getValue()).toStrictEqual(120);
});

// like numpy array
// [
// [1, 0, 1.23, 1, 0],
// [0, 100, 2.34, 0, -3],
// ]
it('nm multiple formats', () => {
const originValueObject = new ArrayValueObject({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false],
[0, '100', '2.34', 'test', -3],
]),
rowCount: 2,
columnCount: 5,
unitId: '',
sheetId: '',
row: 0,
column: 0,
});

expect(originValueObject.sum().getValue()).toStrictEqual(101.57);
});
});

describe('mean', () => {
it('normal', () => {
expect(originArrayValueObject.mean().getValue()).toStrictEqual(8);
Expand All @@ -167,7 +243,7 @@ describe('arrayValueObject test', () => {
column: 0,
});

expect(originValueObject.mean().getValue()).toStrictEqual(10.257);
expect(originValueObject.mean().getValue()).toStrictEqual(16.928333333333335);
});
});

Expand All @@ -176,7 +252,7 @@ describe('arrayValueObject test', () => {
expect(originArrayValueObject.var().getValue()).toStrictEqual(18.666666666666668);
});

it('nm multiple formats', () => {
it('var nm multiple formats', () => {
const originValueObject = new ArrayValueObject({
calculateValueList: transformToValueObject([
[1, ' ', 1.23, true, false],
Expand All @@ -190,7 +266,7 @@ describe('arrayValueObject test', () => {
column: 0,
});

expect(originValueObject.var().getValue()).toStrictEqual(896.592801);
expect(originValueObject.var().getValue()).toStrictEqual(1382.9296138888888);
});
});

Expand All @@ -213,7 +289,7 @@ describe('arrayValueObject test', () => {
column: 0,
});

expect(originValueObject.std().getValue()).toStrictEqual(29.94315950263098);
expect(originValueObject.std().getValue()).toStrictEqual(37.187761614392564);
});
});

Expand Down Expand Up @@ -298,7 +374,11 @@ describe('arrayValueObject test', () => {
});

it('ValueObjectFactory create StringValueObject ', () => {
const stringValueObject = ValueObjectFactory.create('test');
let stringValueObject = ValueObjectFactory.create('test');

expect(stringValueObject.isString()).toBeTruthy();

stringValueObject = ValueObjectFactory.create(' ');

expect(stringValueObject.isString()).toBeTruthy();
});
Expand Down
Expand Up @@ -729,7 +729,8 @@ export class ArrayValueObject extends BaseValueObject {
override sum() {
let accumulatorAll: BaseValueObject = new NumberValueObject(0);
this.iterator((valueObject) => {
if (valueObject == null) {
// 'test', ' ', blank cell, TRUE and FALSE are ignored
if (valueObject == null || valueObject.isString() || valueObject.isBoolean() || valueObject.isNull()) {
return true; // continue
}

Expand Down Expand Up @@ -801,11 +802,8 @@ export class ArrayValueObject extends BaseValueObject {
override count() {
let accumulatorAll: BaseValueObject = new NumberValueObject(0);
this.iterator((valueObject) => {
if (valueObject == null) {
return true; // continue
}

if (valueObject.isError() || valueObject.isString() || valueObject.isNull()) {
// 'test', ' ', blank cell, TRUE and FALSE are ignored
if (valueObject == null || valueObject.isError() || valueObject.isString() || valueObject.isNull() || valueObject.isBoolean()) {
return true; // continue
}
accumulatorAll = accumulatorAll.plusBy(1) as BaseValueObject;
Expand All @@ -817,11 +815,7 @@ export class ArrayValueObject extends BaseValueObject {
override countA() {
let accumulatorAll: BaseValueObject = new NumberValueObject(0);
this.iterator((valueObject) => {
if (valueObject == null) {
return true; // continue
}

if (valueObject.isNull()) {
if (valueObject == null || valueObject.isNull()) {
return true; // continue
}

Expand All @@ -834,7 +828,7 @@ export class ArrayValueObject extends BaseValueObject {
override countBlank() {
let accumulatorAll: BaseValueObject = new NumberValueObject(0);
this.iterator((valueObject) => {
if (valueObject != null) {
if (valueObject != null && !valueObject.isNull()) {
return true; // continue
}

Expand Down Expand Up @@ -1066,8 +1060,8 @@ export class ArrayValueObject extends BaseValueObject {
override mean(): BaseValueObject {
const sum = this.sum();

// Count strings in
const count = this.countA();
// Like sum, ignore strings and booleans
const count = this.count();

return sum.divided(count);
}
Expand Down Expand Up @@ -1095,35 +1089,39 @@ export class ArrayValueObject extends BaseValueObject {
return allValue.get(0, (count - 1) / 2);
}

// TODO ddof, ignore strings and booleans
override var(): BaseValueObject {
const mean = this.mean();

// let isError = null;
const squaredDifferences: BaseValueObject[][] = [];
this.iterator((valueObject: Nullable<BaseValueObject>, row: number, column: number) => {
if (valueObject == null || valueObject.isError() || valueObject.isString()) {
valueObject = new NumberValueObject(0);
const squaredDifferences: BaseValueObject[][] = [[]];
this.iterator((valueObject: Nullable<BaseValueObject>) => {
// for VARPA and VARA, strings and FALSE are counted as 0, TRUE is counted as 1
// for VAR.S/VAR, or VAR.P/VARP, strings,TRUE and FALSE are ignored
// Since sum ignores strings and booleans, they are ignored here too, and VAR.S and VAR.P are used more

// VAR.S assumes that its arguments are a sample of the population, like numpy.var(data, ddof=1)
// VAR.P assumes that its arguments are the entire population, like numpy.var(data, ddof=0)
// numpy.var uses ddof=0 by default, so we use ddof=0 here
if (valueObject == null || valueObject.isError() || valueObject.isString() || valueObject.isBoolean() || valueObject.isNull()) {
return;
}

let baseValueObject = valueObject.minus(mean).pow(new NumberValueObject(2, true));
const baseValueObject = valueObject.minus(mean).pow(new NumberValueObject(2, true));

if (baseValueObject.isError()) {
baseValueObject = new NumberValueObject(0);
}

if (squaredDifferences[row] == null) {
squaredDifferences[row] = [];
return;
}

squaredDifferences[row][column] = baseValueObject;
squaredDifferences[0].push(baseValueObject);
});

const { _rowCount, _columnCount, _unitId, _sheetId, _currentRow, _currentColumn } = this;

const squaredDifferencesArrayObject = new ArrayValueObject({
calculateValueList: squaredDifferences,
rowCount: _rowCount,
columnCount: _columnCount,
rowCount: 1,
columnCount: squaredDifferences[0].length,
unitId: _unitId,
sheetId: _sheetId,
row: _currentRow,
Expand All @@ -1133,6 +1131,14 @@ export class ArrayValueObject extends BaseValueObject {
return squaredDifferencesArrayObject.mean();
}

/**
* STDEV.P: ddof=0, ignore strings and booleans
* STDEV.S: ddof=1, ignore strings and booleans
*
* STDEVPA: ddof=0,
* STDEVA: ddof=1,
* @returns
*/
override std(): BaseValueObject {
const variance = this.var();

Expand Down Expand Up @@ -1433,7 +1439,8 @@ export class ArrayValueObject extends BaseValueObject {
if (currentValue.isError()) {
result[r][column] = currentValue as ErrorValueObject;
} else if (valueObject.isError()) {
result[r][column] = new ErrorValueObject(ErrorType.VALUE);
// 1 + #NAME? gets #NAME?
result[r][column] = valueObject;
} else {
switch (batchOperatorType) {
case BatchOperatorType.PLUS:
Expand Down
Expand Up @@ -18,7 +18,7 @@ import Big from 'big.js';

import { reverseCompareOperator } from '../../basics/calculate';
import { BooleanValue, ConcatenateType } from '../../basics/common';
import { ErrorType } from '../../basics/error-type';
import { ERROR_TYPE_SET, ErrorType } from '../../basics/error-type';
import { compareToken } from '../../basics/token';
import { compareWithWildcard, isWildcard } from '../utils/compare';
import { ceil, floor, pow, round } from '../utils/math-kit';
Expand Down Expand Up @@ -277,11 +277,11 @@ export class BooleanValueObject extends BaseValueObject {
}

override sqrt(): BaseValueObject {
return this._convertTonNumber();
return this._convertTonNumber().sqrt();
}

override cbrt(): BaseValueObject {
return this._convertTonNumber();
return this._convertTonNumber().cbrt();
}

override cos(): BaseValueObject {
Expand Down Expand Up @@ -394,8 +394,10 @@ export class NumberValueObject extends BaseValueObject {
return valueObject.plus(this);
}
const object = this.plusBy(valueObject.getValue());

// = 1 + #NAME? gets #NAME?, = 1 + #VALUE! gets #VALUE!
if (object.isError()) {
return this;
return object;
}

return object;
Expand Down Expand Up @@ -463,6 +465,10 @@ export class NumberValueObject extends BaseValueObject {
override plusBy(value: string | number | boolean): BaseValueObject {
const currentValue = this.getValue();
if (typeof value === 'string') {
// = 1 + #NAME? gets #NAME?, = 1 + #VALUE! gets #VALUE!
if (ERROR_TYPE_SET.has(value as ErrorType)) {
return new ErrorValueObject(value as ErrorType);
}
return new ErrorValueObject(ErrorType.VALUE);
}
if (typeof value === 'number') {
Expand Down

0 comments on commit a13c450

Please sign in to comment.