Skip to content

Commit

Permalink
feat(engine-formula): add iseven isodd (#1873)
Browse files Browse the repository at this point in the history
* feat(engine-formula): add iseven isodd

* feat: register isodd

* chore: fix typo

* feat: deal with i18n

* test: add tests for not convertable situations

* feat: deal with boolean values

* fix: test

* fix: fix array value
  • Loading branch information
wzhudev committed Apr 12, 2024
1 parent bf6f779 commit 21d145c
Show file tree
Hide file tree
Showing 9 changed files with 203 additions and 32 deletions.
Expand Up @@ -18,16 +18,20 @@ import { FUNCTION_NAMES_INFORMATION } from './function-names';
import { Isblank } from './isblank';
import { Iserr } from './iserr';
import { Iserror } from './iserror';
import { Iseven } from './iseven/iseven';
import { Islogical } from './islogical';
import { Isna } from './isna';
import { Isnontext } from './isnontext';
import { Isnumber } from './isnumber';
import { Isodd } from './isodd/isodd';
import { Isref } from './isref';
import { Istext } from './istext';

export const functionInformation = [
[Isblank, FUNCTION_NAMES_INFORMATION.ISBLANK],
[Iserr, FUNCTION_NAMES_INFORMATION.ISERR],
[Iseven, FUNCTION_NAMES_INFORMATION.ISEVEN],
[Isodd, FUNCTION_NAMES_INFORMATION.ISODD],
[Iserror, FUNCTION_NAMES_INFORMATION.ISERROR],
[Islogical, FUNCTION_NAMES_INFORMATION.ISLOGICAL],
[Isna, FUNCTION_NAMES_INFORMATION.ISNA],
Expand Down
@@ -0,0 +1,49 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { describe, expect, it } from 'vitest';

import { FUNCTION_NAMES_INFORMATION } from '../function-names';
import { BooleanValueObject, NumberValueObject, StringValueObject } from '../../../engine/value-object/primitive-object';
import { ErrorType } from '../../../basics/error-type';
import { ArrayValueObject } from '../../../engine/value-object/array-value-object';
import { Iseven } from './iseven';

describe('Test iseven function', () => {
const testFunction = new Iseven(FUNCTION_NAMES_INFORMATION.ISEVEN);

it('should work with different kind of number values', () => {
expect(testFunction.calculate(NumberValueObject.create(-1)).getValue()).toBeFalsy();
expect(testFunction.calculate(NumberValueObject.create(2.5)).getValue()).toBeTruthy();
expect(testFunction.calculate(NumberValueObject.create(5)).getValue()).toBeFalsy();
expect(testFunction.calculate(NumberValueObject.create(0)).getValue()).toBeTruthy();
});

it('should convert value first if is it not a number value', () => {
expect(testFunction.calculate(StringValueObject.create('123')).getValue()).toBeFalsy();
expect(testFunction.calculate(StringValueObject.create('122')).getValue()).toBeTruthy();
});

it('should throw error when value is not convertable to number', () => {
expect(testFunction.calculate(StringValueObject.create('not')).getValue()).toBe(ErrorType.VALUE);
expect(testFunction.calculate(BooleanValueObject.create(true)).getValue()).toBe(ErrorType.VALUE);
expect(testFunction.calculate(BooleanValueObject.create(false)).getValue()).toBe(ErrorType.VALUE);
});

it('should throw error for array values', () => {
expect(testFunction.calculate(ArrayValueObject.create('A1:C1')).getValue()).toBe(ErrorType.VALUE);
});
});
44 changes: 44 additions & 0 deletions packages/engine-formula/src/functions/information/iseven/iseven.ts
@@ -0,0 +1,44 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { ErrorType } from '../../../basics/error-type';
import type { BaseValueObject } from '../../../engine/value-object/base-value-object';
import { ErrorValueObject } from '../../../engine/value-object/base-value-object';
import { BooleanValueObject } from '../../../engine/value-object/primitive-object';
import { BaseFunction } from '../../base-function';

export class Iseven extends BaseFunction {
override calculate(value: BaseValueObject) {
if (value == null) {
return ErrorValueObject.create(ErrorType.NA);
}

if (value.isArray() || value.isBoolean()) {
return ErrorValueObject.create(ErrorType.VALUE);
}

if (!value.isNumber()) {
value = value.convertToNumberObjectValue();
if (!value.isNumber()) {
return ErrorValueObject.create(ErrorType.VALUE);
}
}

const val = value.getValue() as number;
const floored = Math.floor(Math.abs(val));
return BooleanValueObject.create(floored % 2 === 0);
}
}
@@ -0,0 +1,50 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { describe, expect, it } from 'vitest';
import { FUNCTION_NAMES_INFORMATION } from '../function-names';

import { BooleanValueObject, NumberValueObject, StringValueObject } from '../../../engine/value-object/primitive-object';
import { ErrorType } from '../../../basics/error-type';
import { ArrayValueObject } from '../../../engine/value-object/array-value-object';
import { Isodd } from './isodd';


describe('Test isodd function', () => {
const testFunction = new Isodd(FUNCTION_NAMES_INFORMATION.ISODD);

it('should work with different kind of number values', () => {
expect(testFunction.calculate(NumberValueObject.create(-1)).getValue()).toBeTruthy();
expect(testFunction.calculate(NumberValueObject.create(2.5)).getValue()).toBeFalsy();
expect(testFunction.calculate(NumberValueObject.create(5)).getValue()).toBeTruthy();
expect(testFunction.calculate(NumberValueObject.create(0)).getValue()).toBeFalsy();
});

it('should convert value first if is it not a number value', () => {
expect(testFunction.calculate(StringValueObject.create('123')).getValue()).toBeTruthy();
expect(testFunction.calculate(StringValueObject.create('122')).getValue()).toBeFalsy();
});

it('should throw error when value is not convertable to number', () => {
expect(testFunction.calculate(StringValueObject.create('not')).getValue()).toBe(ErrorType.VALUE);
expect(testFunction.calculate(BooleanValueObject.create(true)).getValue()).toBe(ErrorType.VALUE);
expect(testFunction.calculate(BooleanValueObject.create(false)).getValue()).toBe(ErrorType.VALUE);
});

it('should throw error for array values', () => {
expect(testFunction.calculate(ArrayValueObject.create('A1:C1')).getValue()).toBe(ErrorType.VALUE);
});
});
44 changes: 44 additions & 0 deletions packages/engine-formula/src/functions/information/isodd/isodd.ts
@@ -0,0 +1,44 @@
/**
* Copyright 2023-present DreamNum Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { ErrorType } from '../../../basics/error-type';
import type { BaseValueObject } from '../../../engine/value-object/base-value-object';
import { ErrorValueObject } from '../../../engine/value-object/base-value-object';
import { BooleanValueObject } from '../../../engine/value-object/primitive-object';
import { BaseFunction } from '../../base-function';

export class Isodd extends BaseFunction {
override calculate(value: BaseValueObject) {
if (value == null) {
return ErrorValueObject.create(ErrorType.NA);
}

if (value.isArray() || value.isBoolean()) {
return ErrorValueObject.create(ErrorType.VALUE);
}

if (!value.isNumber()) {
value = value.convertToNumberObjectValue();
if (!value.isNumber()) {
return ErrorValueObject.create(ErrorType.VALUE);
}
}

const val = value.getValue() as number;
const floored = Math.floor(Math.abs(val));
return BooleanValueObject.create(floored % 2 !== 0);
}
}
Expand Up @@ -106,8 +106,7 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
value: { name: 'value', detail: 'The value to test. If number is not an integer, it is truncated.' },
},
},
ISFORMULA: {
Expand Down Expand Up @@ -186,8 +185,7 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
value: { name: 'value', detail: 'The value to test. If number is not an integer, it is truncated.' },
},
},
ISOMITTED: {
Expand Down
Expand Up @@ -108,8 +108,7 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
value: { name: '値', detail: '検定する値を指定します。 数値が整数でない場合は、小数点以下が切り捨てられます。' },
},
},
ISFORMULA: {
Expand Down Expand Up @@ -192,8 +191,7 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
value: { name: '値', detail: '検定する値を指定します。 数値が整数でない場合は、小数点以下が切り捨てられます。' },
},
},
ISOMITTED: {
Expand Down
Expand Up @@ -80,7 +80,7 @@ export default {
},
],
functionParameter: {
value: { name: '值', detail: ' 指的是要测试的值。 参数 value 可以是空白(空单元格)、错误值、逻辑值、文本、数字、引用值,或者引用要测试的以上任意值的名称。' },
value: { name: '值', detail: '指的是要测试的值。 参数 value 可以是空白(空单元格)、错误值、逻辑值、文本、数字、引用值,或者引用要测试的以上任意值的名称。' },
},
},
ISERROR: {
Expand All @@ -93,7 +93,7 @@ export default {
},
],
functionParameter: {
value: { name: '值', detail: ' 指的是要测试的值。 参数 value 可以是空白(空单元格)、错误值、逻辑值、文本、数字、引用值,或者引用要测试的以上任意值的名称。' },
value: { name: '值', detail: '指的是要测试的值。 参数 value 可以是空白(空单元格)、错误值、逻辑值、文本、数字、引用值,或者引用要测试的以上任意值的名称。' },
},
},
ISEVEN: {
Expand All @@ -106,8 +106,7 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
value: { name: '值', detail: '要测试的值。如果 number 不是整数,将被截尾取整。' },
},
},
ISFORMULA: {
Expand Down Expand Up @@ -186,8 +185,7 @@ export default {
},
],
functionParameter: {
number1: { name: 'number1', detail: 'first' },
number2: { name: 'number2', detail: 'second' },
value: { name: '值', detail: '要测试的值。如果 number 不是整数,将被截尾取整。' },
},
},
ISOMITTED: {
Expand Down
22 changes: 4 additions & 18 deletions packages/sheets-formula/src/services/function-list/information.ts
Expand Up @@ -135,15 +135,8 @@ export const FUNCTION_LIST_INFORMATION: IFunctionInfo[] = [
abstract: 'formula.functionList.ISEVEN.abstract',
functionParameter: [
{
name: 'formula.functionList.ISEVEN.functionParameter.number1.name',
detail: 'formula.functionList.ISEVEN.functionParameter.number1.detail',
example: 'A1:A20',
require: 1,
repeat: 0,
},
{
name: 'formula.functionList.ISEVEN.functionParameter.number2.name',
detail: 'formula.functionList.ISEVEN.functionParameter.number2.detail',
name: 'formula.functionList.ISEVEN.functionParameter.value.name',
detail: 'formula.functionList.ISEVEN.functionParameter.value.detail',
example: 'A1:A20',
require: 1,
repeat: 0,
Expand Down Expand Up @@ -239,15 +232,8 @@ export const FUNCTION_LIST_INFORMATION: IFunctionInfo[] = [
abstract: 'formula.functionList.ISODD.abstract',
functionParameter: [
{
name: 'formula.functionList.ISODD.functionParameter.number1.name',
detail: 'formula.functionList.ISODD.functionParameter.number1.detail',
example: 'A1:A20',
require: 1,
repeat: 0,
},
{
name: 'formula.functionList.ISODD.functionParameter.number2.name',
detail: 'formula.functionList.ISODD.functionParameter.number2.detail',
name: 'formula.functionList.ISODD.functionParameter.value.name',
detail: 'formula.functionList.ISODD.functionParameter.value.detail',
example: 'A1:A20',
require: 1,
repeat: 0,
Expand Down

0 comments on commit 21d145c

Please sign in to comment.